status bar
git clone https://git.lucas.co/cce-status-interface.git
Refactor status interface and main loop
Cargo.lock | 587 +++++++++++++++++++++++++++------------------------
Cargo.toml | 7 +-
src/bin/dbus_test.rs | 181 ++++++++++++++++
src/main.rs | 563 ++++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 1019 insertions(+), 319 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index da8327a..6caa068 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,6 +18,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
[[package]]
name = "ahash"
version = "0.8.12"
@@ -31,15 +37,6 @@ dependencies = [
"zerocopy",
]
-[[package]]
-name = "aho-corasick"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "android-activity"
version = "0.6.1"
@@ -74,12 +71,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "anyhow"
-version = "1.0.102"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
-
[[package]]
name = "arrayref"
version = "0.3.9"
@@ -109,24 +100,26 @@ dependencies = [
[[package]]
name = "async-broadcast"
-version = "0.4.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener",
+ "event-listener-strategy",
"futures-core",
- "parking_lot",
+ "pin-project-lite",
]
[[package]]
name = "async-channel"
-version = "1.9.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
- "event-listener",
+ "event-listener-strategy",
"futures-core",
+ "pin-project-lite",
]
[[package]]
@@ -143,24 +136,91 @@ dependencies = [
"slab",
]
+[[package]]
+name = "async-fs"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
+dependencies = [
+ "async-lock",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix 1.1.4",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "async-lock"
-version = "2.8.0"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
"event-listener",
+ "futures-lite",
+ "rustix 1.1.4",
]
[[package]]
name = "async-recursion"
-version = "0.3.2"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix 1.1.4",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -177,7 +237,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -228,6 +288,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "block2"
version = "0.5.1"
@@ -237,6 +306,19 @@ dependencies = [
"objc2",
]
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
[[package]]
name = "bumpalo"
version = "3.20.3"
@@ -260,15 +342,9 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
[[package]]
name = "bytes"
version = "1.11.1"
@@ -346,14 +422,15 @@ dependencies = [
"chrono",
"clear-ui",
"glyphon",
+ "png",
"pollster",
"serde",
"serde_json",
- "stray",
"tokio",
"tokio-stream",
"wgpu",
"winit",
+ "zbus",
]
[[package]]
@@ -460,6 +537,24 @@ dependencies = [
"unicode-segmentation",
]
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
@@ -486,40 +581,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
-name = "cursor-icon"
-version = "1.2.0"
+name = "crypto-common"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
-
-[[package]]
-name = "derivative"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
+ "generic-array",
+ "typenum",
]
[[package]]
-name = "dirs"
-version = "4.0.0"
+name = "cursor-icon"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
-dependencies = [
- "dirs-sys",
-]
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
[[package]]
-name = "dirs-sys"
-version = "0.3.7"
+name = "digest"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "libc",
- "redox_users",
- "winapi",
+ "block-buffer",
+ "crypto-common",
]
[[package]]
@@ -564,6 +648,12 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
+[[package]]
+name = "endi"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
+
[[package]]
name = "enumflags2"
version = "0.7.12"
@@ -582,7 +672,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -622,9 +712,24 @@ dependencies = [
[[package]]
name = "event-listener"
-version = "2.5.3"
+version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
[[package]]
name = "fastrand"
@@ -632,12 +737,31 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+[[package]]
+name = "flate2"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
[[package]]
name = "foldhash"
version = "0.1.5"
@@ -694,7 +818,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -747,12 +871,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
+ "futures-io",
"futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"slab",
]
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
[[package]]
name = "gethostname"
version = "1.1.0"
@@ -988,7 +1124,7 @@ dependencies = [
"quote",
"rustc_version",
"simd_cesu8",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -1016,7 +1152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
dependencies = [
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -1058,12 +1194,6 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
-[[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-
[[package]]
name = "libc"
version = "0.2.186"
@@ -1161,15 +1291,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "memoffset"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
-dependencies = [
- "autocfg",
-]
-
[[package]]
name = "memoffset"
version = "0.9.1"
@@ -1194,6 +1315,16 @@ dependencies = [
"paste",
]
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
[[package]]
name = "mio"
version = "1.2.0"
@@ -1268,15 +1399,15 @@ dependencies = [
[[package]]
name = "nix"
-version = "0.23.2"
+version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
- "bitflags 1.3.2",
- "cc",
+ "bitflags 2.11.1",
"cfg-if",
+ "cfg_aliases",
"libc",
- "memoffset 0.6.5",
+ "memoffset",
]
[[package]]
@@ -1304,10 +1435,10 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [
- "proc-macro-crate 3.5.0",
+ "proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -1549,9 +1680,9 @@ dependencies = [
[[package]]
name = "ordered-stream"
-version = "0.0.1"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -1624,7 +1755,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -1633,6 +1764,17 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+[[package]]
+name = "piper"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.33"
@@ -1645,6 +1787,19 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
[[package]]
name = "polling"
version = "3.11.0"
@@ -1680,23 +1835,13 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
-[[package]]
-name = "proc-macro-crate"
-version = "1.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
-dependencies = [
- "once_cell",
- "toml_edit 0.19.15",
-]
-
[[package]]
name = "proc-macro-crate"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [
- "toml_edit 0.25.11+spec-1.1.0",
+ "toml_edit",
]
[[package]]
@@ -1843,46 +1988,6 @@ dependencies = [
"bitflags 2.11.1",
]
-[[package]]
-name = "redox_users"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
-dependencies = [
- "getrandom 0.2.17",
- "libredox",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "regex"
-version = "1.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
-
[[package]]
name = "renderdoc-sys"
version = "1.1.0"
@@ -2038,7 +2143,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2062,24 +2167,20 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
name = "sha1"
-version = "0.6.1"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
- "sha1_smol",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
]
-[[package]]
-name = "sha1_smol"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
-
[[package]]
name = "shlex"
version = "1.3.0"
@@ -2096,6 +2197,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "simd-adler32"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+
[[package]]
name = "simd_cesu8"
version = "1.1.1"
@@ -2202,24 +2309,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-[[package]]
-name = "stray"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "358c1637c5ba4ccf1b6a0698de81454db644866cc426d1abc6d357b2efede511"
-dependencies = [
- "anyhow",
- "byteorder",
- "chrono",
- "log",
- "serde",
- "thiserror 1.0.69",
- "tokio",
- "tokio-stream",
- "tracing",
- "zbus",
-]
-
[[package]]
name = "strict-num"
version = "0.1.1"
@@ -2245,7 +2334,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2265,17 +2354,6 @@ dependencies = [
"zeno",
]
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
[[package]]
name = "syn"
version = "2.0.117"
@@ -2344,7 +2422,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2355,7 +2433,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2423,7 +2501,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2437,12 +2515,6 @@ dependencies = [
"tokio",
]
-[[package]]
-name = "toml_datetime"
-version = "0.6.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
-
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
@@ -2452,17 +2524,6 @@ dependencies = [
"serde_core",
]
-[[package]]
-name = "toml_edit"
-version = "0.19.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
-dependencies = [
- "indexmap",
- "toml_datetime 0.6.11",
- "winnow 0.5.40",
-]
-
[[package]]
name = "toml_edit"
version = "0.25.11+spec-1.1.0"
@@ -2470,9 +2531,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b"
dependencies = [
"indexmap",
- "toml_datetime 1.1.1+spec-1.1.0",
+ "toml_datetime",
"toml_parser",
- "winnow 1.0.3",
+ "winnow",
]
[[package]]
@@ -2481,7 +2542,7 @@ version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
- "winnow 1.0.3",
+ "winnow",
]
[[package]]
@@ -2503,7 +2564,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2533,13 +2594,19 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+[[package]]
+name = "typenum"
+version = "1.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
+
[[package]]
name = "uds_windows"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
- "memoffset 0.9.1",
+ "memoffset",
"tempfile",
"windows-sys 0.61.2",
]
@@ -2677,7 +2744,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
"wasm-bindgen-shared",
]
@@ -2928,22 +2995,6 @@ dependencies = [
"web-sys",
]
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
[[package]]
name = "winapi-util"
version = "0.1.11"
@@ -2953,12 +3004,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-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"
version = "0.58.0"
@@ -3003,7 +3048,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3014,7 +3059,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3025,7 +3070,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3036,7 +3081,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3225,15 +3270,6 @@ dependencies = [
"xkbcommon-dl",
]
-[[package]]
-name = "winnow"
-version = "0.5.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "winnow"
version = "1.0.3"
@@ -3287,6 +3323,16 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
+[[package]]
+name = "xdg-home"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "xkbcommon-dl"
version = "0.4.2"
@@ -3320,39 +3366,37 @@ checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
[[package]]
name = "zbus"
-version = "2.3.2"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d8f1a037b2c4a67d9654dc7bdfa8ff2e80555bbefdd3c1833c1d1b27c963a6b"
+checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
dependencies = [
"async-broadcast",
- "async-channel",
"async-executor",
+ "async-fs",
+ "async-io",
"async-lock",
+ "async-process",
"async-recursion",
"async-task",
"async-trait",
- "byteorder",
- "derivative",
- "dirs",
+ "blocking",
"enumflags2",
"event-listener",
"futures-core",
"futures-sink",
"futures-util",
"hex",
- "lazy_static",
"nix",
- "once_cell",
"ordered-stream",
"rand",
"serde",
"serde_repr",
"sha1",
"static_assertions",
- "tokio",
"tracing",
"uds_windows",
- "winapi",
+ "windows-sys 0.52.0",
+ "xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
@@ -3360,22 +3404,22 @@ dependencies = [
[[package]]
name = "zbus_macros"
-version = "2.3.2"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f8fb5186d1c87ae88cf234974c240671238b4a679158ad3b94ec465237349a6"
+checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
dependencies = [
- "proc-macro-crate 1.3.1",
+ "proc-macro-crate",
"proc-macro2",
"quote",
- "regex",
- "syn 1.0.109",
+ "syn",
+ "zvariant_utils",
]
[[package]]
name = "zbus_names"
-version = "2.6.1"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
+checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
dependencies = [
"serde",
"static_assertions",
@@ -3405,7 +3449,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3416,13 +3460,12 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zvariant"
-version = "3.15.2"
+version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db"
+checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
dependencies = [
- "byteorder",
+ "endi",
"enumflags2",
- "libc",
"serde",
"static_assertions",
"zvariant_derive",
@@ -3430,24 +3473,24 @@ dependencies = [
[[package]]
name = "zvariant_derive"
-version = "3.15.2"
+version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9"
+checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
dependencies = [
- "proc-macro-crate 1.3.1",
+ "proc-macro-crate",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
-version = "1.0.1"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
+checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn",
]
diff --git a/Cargo.toml b/Cargo.toml
index 902cea7..cdc0dfa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,11 @@ tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = "0.4"
-stray = "0.1"
tokio-stream = "0.1"
+zbus = "4"
+png = "0.17"
+
+
+
+
diff --git a/src/bin/dbus_test.rs b/src/bin/dbus_test.rs
new file mode 100644
index 0000000..ab20bd3
--- /dev/null
+++ b/src/bin/dbus_test.rs
@@ -0,0 +1,181 @@
+use zbus::{proxy, Connection};
+
+#[derive(Debug, Clone)]
+pub struct NotifierAddress {
+ pub destination: String,
+ pub path: String,
+}
+
+impl NotifierAddress {
+ pub fn from_notifier_service(service: &str, sender: &str) -> Result<Self, String> {
+ if service.starts_with('/') {
+ Ok(NotifierAddress {
+ destination: sender.to_string(),
+ path: service.to_string(),
+ })
+ } else if let Some((destination, path)) = service.split_once('/') {
+ Ok(NotifierAddress {
+ destination: destination.to_string(),
+ path: format!("/{}", path),
+ })
+ } else if service.contains(':') {
+ let split = service.split(':').collect::<Vec<&str>>();
+ Ok(NotifierAddress {
+ destination: format!(":{}", split[1]),
+ path: "/StatusNotifierItem".to_string(),
+ })
+ } else {
+ Ok(NotifierAddress {
+ destination: service.to_string(),
+ path: "/StatusNotifierItem".to_string(),
+ })
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct TrayPixmap {
+ pub width: i32,
+ pub height: i32,
+ pub pixels: Vec<u8>,
+}
+
+#[derive(Debug, Clone)]
+pub struct TrayItem {
+ pub id: String,
+ pub icon_name: Option<String>,
+ pub icon_theme_path: Option<String>,
+ pub pixmaps: Option<Vec<TrayPixmap>>,
+ pub title: Option<String>,
+}
+
+#[proxy(
+ interface = "org.kde.StatusNotifierItem",
+ default_path = "/StatusNotifierItem"
+)]
+trait StatusNotifierItem {
+ #[zbus(property)]
+ fn id(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn category(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn status(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn title(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn icon_name(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn icon_theme_path(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn icon_pixmap(&self) -> zbus::Result<Vec<(i32, i32, Vec<u8>)>>;
+}
+
+#[proxy(
+ interface = "org.kde.StatusNotifierWatcher",
+ default_path = "/StatusNotifierWatcher"
+)]
+trait StatusNotifierWatcher {
+ #[zbus(property)]
+ fn registered_status_notifier_items(&self) -> zbus::Result<Vec<String>>;
+}
+
+async fn fetch_tray_item(conn: &Connection, addr: &NotifierAddress) -> Result<TrayItem, zbus::Error> {
+ let proxy = StatusNotifierItemProxy::builder(conn)
+ .destination(addr.destination.clone())?
+ .path(addr.path.clone())?
+ .build()
+ .await?;
+
+ println!("Querying properties for destination='{}', path='{}'...", addr.destination, addr.path);
+
+ let id_res = proxy.id().await;
+ println!(" id result: {:?}", id_res);
+
+ let category_res = proxy.category().await;
+ println!(" category result: {:?}", category_res);
+
+ let status_res = proxy.status().await;
+ println!(" status result: {:?}", status_res);
+
+ let icon_name_res = proxy.icon_name().await;
+ println!(" icon_name result: {:?}", icon_name_res);
+
+ let icon_theme_path_res = proxy.icon_theme_path().await;
+ println!(" icon_theme_path result: {:?}", icon_theme_path_res);
+
+ let title_res = proxy.title().await;
+ println!(" title result: {:?}", title_res);
+
+ let pixmap_res = proxy.icon_pixmap().await;
+ println!(" pixmap result: ({} pixmaps or error: {:?})",
+ pixmap_res.as_ref().map(|v| v.len()).unwrap_or(0),
+ pixmap_res.as_ref().err()
+ );
+
+ let id = format!("{}/{}", addr.destination, addr.path.trim_start_matches('/'));
+ let icon_name = icon_name_res.ok();
+ let icon_theme_path = icon_theme_path_res.ok();
+ let title = title_res.ok();
+
+ let pixmaps = pixmap_res.ok().map(|v| {
+ v.into_iter()
+ .map(|(w, h, pixels)| TrayPixmap {
+ width: w,
+ height: h,
+ pixels,
+ })
+ .collect()
+ });
+
+ Ok(TrayItem {
+ id,
+ icon_name,
+ icon_theme_path,
+ pixmaps,
+ title,
+ })
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let conn = Connection::session().await?;
+
+ let watcher_proxy = StatusNotifierWatcherProxy::builder(&conn)
+ .destination("org.kde.StatusNotifierWatcher")?
+ .path("/StatusNotifierWatcher")?
+ .build()
+ .await?;
+
+ let items = watcher_proxy.registered_status_notifier_items().await?;
+ println!("Registered status notifier items from watcher: {:?}", items);
+
+ for item in items {
+ println!("======================================");
+ // An item string can be like ":1.5631/org/ayatana/NotificationItem/dropbox_client_1112673"
+ // Let's parse it using split_once('/')
+ let addr = if let Some((destination, path)) = item.split_once('/') {
+ NotifierAddress {
+ destination: destination.to_string(),
+ path: format!("/{}", path),
+ }
+ } else {
+ NotifierAddress {
+ destination: item.clone(),
+ path: "/StatusNotifierItem".to_string(),
+ }
+ };
+
+ match fetch_tray_item(&conn, &addr).await {
+ Ok(item) => println!("SUCCESS: {:?}", item.id),
+ Err(e) => println!("ERROR for {}: {:?}", item, e),
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index cf34851..eda6875 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,8 +10,10 @@ use glyphon::{
TextAtlas, TextBounds, TextRenderer, Viewport,
};
use clear_ui::color;
-use stray::StatusNotifierWatcher;
-use stray::message::NotifierItemMessage;
+// zbus D-Bus Integration
+
+
+
#[derive(Debug, Clone)]
struct TrayPixmap {
@@ -505,11 +507,13 @@ impl StatusApp {
// 5b. System Tray Icons (render to the left of the CPU/stats block)
self.tray_item_bounds.clear();
if !self.tray_items.is_empty() {
+ println!("[status-tray-render] Rendering {} tray items (screen size: {}x{})", self.tray_items.len(), sw, sh);
right_x -= 16.0 * s; // Separator padding
let mut sorted_tray: Vec<&TrayItem> = self.tray_items.values().collect();
sorted_tray.sort_by_key(|item| &item.id);
for item in sorted_tray.iter().rev() {
+ println!("[status-tray-render] Item ID: '{}', icon_name: {:?}, pixmaps is Some: {}", item.id, item.icon_name, item.pixmaps.is_some());
let icon_size = 16.0 * s;
right_x -= icon_size;
let x = right_x;
@@ -532,17 +536,60 @@ impl StatusApp {
// Find pixmap closest to 16 pixels wide
if let Some(pixmap) = pixmaps.iter().min_by_key(|p| (p.width - 16).abs()) {
if pixmap.width > 0 && pixmap.height > 0 {
- let pixel_w = icon_size / pixmap.width as f32;
- let pixel_h = icon_size / pixmap.height as f32;
+ // Calculate average brightness of visible pixels to see if we need to recolor
+ let mut total_brightness = 0.0;
+ let mut visible_pixel_count = 0;
for row in 0..pixmap.height {
for col in 0..pixmap.width {
let idx = ((row * pixmap.width + col) * 4) as usize;
if idx + 3 < pixmap.pixels.len() {
let a = pixmap.pixels[idx] as f32 / 255.0;
- if a > 0.0 {
+ if a > 0.1 {
let r = pixmap.pixels[idx + 1] as f32 / 255.0;
let g = pixmap.pixels[idx + 2] as f32 / 255.0;
let b = pixmap.pixels[idx + 3] as f32 / 255.0;
+ total_brightness += (r + g + b) / 3.0;
+ visible_pixel_count += 1;
+ }
+ }
+ }
+ }
+
+ let avg_brightness = if visible_pixel_count > 0 {
+ total_brightness / visible_pixel_count as f32
+ } else {
+ 0.5
+ };
+ // If the average brightness is dark, recolor it to be light
+ let recolor_light = avg_brightness < 0.35;
+
+ // Downsample to 16x16 quads to optimize rendering
+ let draw_w = 16;
+ let draw_h = 16;
+ let pixel_w = icon_size / draw_w as f32;
+ let pixel_h = icon_size / draw_h as f32;
+ let mut pushed_pixels = 0;
+ for row in 0..draw_h {
+ for col in 0..draw_w {
+ let src_row = row * pixmap.height / draw_h;
+ let src_col = col * pixmap.width / draw_w;
+ let idx = ((src_row * pixmap.width + src_col) * 4) as usize;
+ if idx + 3 < pixmap.pixels.len() {
+ let a = pixmap.pixels[idx] as f32 / 255.0;
+ if a > 0.0 {
+ let mut r = pixmap.pixels[idx + 1] as f32 / 255.0;
+ let mut g = pixmap.pixels[idx + 2] as f32 / 255.0;
+ let mut b = pixmap.pixels[idx + 3] as f32 / 255.0;
+
+ if recolor_light {
+ let l = (r + g + b) / 3.0;
+ // Map 0.0 (black) to 0.85 (light grey), 1.0 stays 1.0
+ let new_l = 0.85 + (1.0 - 0.85) * l;
+ r = new_l;
+ g = new_l;
+ b = new_l;
+ }
+
self.rects.push(RectWidget {
x: x + col as f32 * pixel_w,
y: y + row as f32 * pixel_h,
@@ -550,16 +597,20 @@ impl StatusApp {
h: pixel_h,
color: [r, g, b, a],
});
+ pushed_pixels += 1;
}
}
}
}
+ println!("[status-tray-render] Item '{}' drawing downsampled pixmap width={}, height={}, icon_size={}, x={}, y={}, pixel_size={}x{}, pushed_rects={}",
+ item.id, pixmap.width, pixmap.height, icon_size, x, y, pixel_w, pixel_h, pushed_pixels);
drawn_pixmap = true;
}
}
}
}
+ println!("[status-tray-render] Item '{}' drawn_pixmap: {}", item.id, drawn_pixmap);
// Fallback to text icon symbol
if !drawn_pixmap {
let symbol = if let Some(ref name) = item.icon_name {
@@ -578,6 +629,8 @@ impl StatusApp {
"💬"
} else if name_lower.contains("steam") || name_lower.contains("game") {
"🎮"
+ } else if name_lower.contains("dropbox") {
+ "📦"
} else {
"âš™"
}
@@ -910,55 +963,474 @@ async fn spawn_system_stats(proxy: EventLoopProxy<CustomEvent>) {
}
}
-async fn spawn_status_tray(
- proxy: EventLoopProxy<CustomEvent>,
- cmd_rx: tokio::sync::mpsc::Receiver<stray::message::NotifierItemCommand>,
-) {
- let watcher = match StatusNotifierWatcher::new(cmd_rx).await {
- Ok(w) => w,
+#[derive(Debug, Clone)]
+pub struct NotifierAddress {
+ pub destination: String,
+ pub path: String,
+}
+
+impl NotifierAddress {
+ pub fn from_notifier_service(service: &str, sender: &str) -> Result<Self, String> {
+ if service.starts_with('/') {
+ Ok(NotifierAddress {
+ destination: sender.to_string(),
+ path: service.to_string(),
+ })
+ } else if let Some((destination, path)) = service.split_once('/') {
+ Ok(NotifierAddress {
+ destination: destination.to_string(),
+ path: format!("/{}", path),
+ })
+ } else if service.contains(':') {
+ let split = service.split(':').collect::<Vec<&str>>();
+ Ok(NotifierAddress {
+ destination: format!(":{}", split[1]),
+ path: "/StatusNotifierItem".to_string(),
+ })
+ } else {
+ Ok(NotifierAddress {
+ destination: service.to_string(),
+ path: "/StatusNotifierItem".to_string(),
+ })
+ }
+ }
+}
+
+#[zbus::proxy(
+ interface = "org.kde.StatusNotifierItem",
+ default_path = "/StatusNotifierItem"
+)]
+trait StatusNotifierItem {
+ #[zbus(property)]
+ fn id(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn category(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn status(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn title(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn icon_name(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn icon_theme_path(&self) -> zbus::Result<String>;
+
+ #[zbus(property)]
+ fn icon_pixmap(&self) -> zbus::Result<Vec<(i32, i32, Vec<u8>)>>;
+
+ #[zbus(signal)]
+ fn new_icon(&self) -> zbus::Result<()>;
+ #[zbus(signal)]
+ fn new_title(&self) -> zbus::Result<()>;
+
+ #[zbus(signal)]
+ fn new_status(&self) -> zbus::Result<()>;
+}
+
+fn find_icon_file(dir: &std::path::Path, icon_name: &str) -> Option<std::path::PathBuf> {
+ if let Ok(entries) = std::fs::read_dir(dir) {
+ for entry in entries.filter_map(Result::ok) {
+ if let Ok(file_type) = entry.file_type() {
+ let path = entry.path();
+ if file_type.is_dir() {
+ if !file_type.is_symlink() {
+ if let Some(found) = find_icon_file(&path, icon_name) {
+ return Some(found);
+ }
+ }
+ } else if file_type.is_file() {
+ if let Some(file_name) = path.file_name().and_then(|f| f.to_str()) {
+ if file_name == format!("{}.png", icon_name) {
+ return Some(path);
+ }
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+fn load_png_as_pixmap(path: &std::path::Path) -> Option<TrayPixmap> {
+ let file = std::fs::File::open(path).ok()?;
+ let mut decoder = png::Decoder::new(file);
+ decoder.set_transformations(png::Transformations::EXPAND);
+ let mut reader = decoder.read_info().ok()?;
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).ok()?;
+
+ let width = info.width as i32;
+ let height = info.height as i32;
+ println!("[status-tray] PNG info: width={}, height={}, color_type={:?}, buffer_size={}", width, height, info.color_type, info.buffer_size());
+ let mut argb_pixels = Vec::with_capacity((width * height * 4) as usize);
+
+ let actual_bytes = &buf[..info.buffer_size()];
+ let mut printed = 0;
+ match info.color_type {
+ png::ColorType::Rgba => {
+ for (i, chunk) in actual_bytes.chunks_exact(4).enumerate() {
+ if chunk[3] > 10 && printed < 5 {
+ println!("[status-tray] Pixel {}: original RGBA=[{}, {}, {}, {}]", i, chunk[0], chunk[1], chunk[2], chunk[3]);
+ printed += 1;
+ }
+ argb_pixels.push(chunk[3]); // A
+ argb_pixels.push(chunk[0]); // R
+ argb_pixels.push(chunk[1]); // G
+ argb_pixels.push(chunk[2]); // B
+ }
+ }
+ png::ColorType::Rgb => {
+ for chunk in actual_bytes.chunks_exact(3) {
+ argb_pixels.push(255); // A
+ argb_pixels.push(chunk[0]); // R
+ argb_pixels.push(chunk[1]); // G
+ argb_pixels.push(chunk[2]); // B
+ }
+ }
+ png::ColorType::Grayscale => {
+ for &g in actual_bytes {
+ argb_pixels.push(255); // A
+ argb_pixels.push(g); // R
+ argb_pixels.push(g); // G
+ argb_pixels.push(g); // B
+ }
+ }
+ png::ColorType::GrayscaleAlpha => {
+ for chunk in actual_bytes.chunks_exact(2) {
+ argb_pixels.push(chunk[1]); // A
+ argb_pixels.push(chunk[0]); // R
+ argb_pixels.push(chunk[0]); // G
+ argb_pixels.push(chunk[0]); // B
+ }
+ }
+ _ => return None,
+ }
+
+ Some(TrayPixmap {
+ width,
+ height,
+ pixels: argb_pixels,
+ })
+}
+
+fn resolve_icon_path(theme_path: Option<&str>, icon_name: &str) -> Option<std::path::PathBuf> {
+ if icon_name.is_empty() {
+ return None;
+ }
+
+ let icon_name = if icon_name == "dropbox" { "dropboxstatus-idle" } else { icon_name };
+
+ if let Some(path_str) = theme_path {
+ if !path_str.is_empty() {
+ let path = std::path::Path::new(path_str);
+ if path.exists() {
+ if let Some(found) = find_icon_file(path, icon_name) {
+ return Some(found);
+ }
+ }
+ }
+ }
+
+ let mut search_dirs = Vec::new();
+ if let Ok(home) = std::env::var("HOME") {
+ search_dirs.push(format!("{}/.local/share/icons", home));
+ search_dirs.push(format!("{}/.icons", home));
+ }
+ search_dirs.push("/usr/share/icons".to_string());
+ search_dirs.push("/usr/share/pixmaps".to_string());
+
+ let sub_paths = [
+ "hicolor/16x16/status",
+ "hicolor/22x22/status",
+ "hicolor/24x24/status",
+ "hicolor/32x32/status",
+ "hicolor/48x48/status",
+ "hicolor/16x16/apps",
+ "hicolor/22x22/apps",
+ "hicolor/24x24/apps",
+ "hicolor/32x32/apps",
+ "hicolor/48x48/apps",
+ "gnome/16x16/status",
+ "gnome/22x22/status",
+ "gnome/24x24/status",
+ "gnome/32x32/status",
+ "gnome/48x48/status",
+ "gnome/16x16/apps",
+ "gnome/22x22/apps",
+ "gnome/24x24/apps",
+ "gnome/32x32/apps",
+ "gnome/48x48/apps",
+ ];
+
+ for base in &search_dirs {
+ for sub in &sub_paths {
+ let path = std::path::Path::new(base).join(sub).join(format!("{}.png", icon_name));
+ if path.exists() && path.is_file() {
+ return Some(path);
+ }
+ }
+ let base_path = std::path::Path::new(base);
+ if base_path.exists() {
+ if let Some(found) = find_icon_file(base_path, icon_name) {
+ return Some(found);
+ }
+ }
+ }
+
+ None
+}
+
+async fn fetch_tray_item(conn: &zbus::Connection, addr: &NotifierAddress) -> Result<TrayItem, zbus::Error> {
+ let proxy = StatusNotifierItemProxy::builder(conn)
+ .destination(addr.destination.clone())?
+ .path(addr.path.clone())?
+ .build()
+ .await?;
+
+ let id = format!("{}/{}", addr.destination, addr.path.trim_start_matches('/'));
+ let icon_name = proxy.icon_name().await.ok();
+ let icon_theme_path = proxy.icon_theme_path().await.ok();
+ let title = proxy.title().await.ok();
+
+ let mut pixmaps = proxy.icon_pixmap().await.ok().and_then(|v| {
+ if v.is_empty() || (v.len() == 1 && v[0].0 == 0 && v[0].1 == 0) {
+ None
+ } else {
+ Some(v.into_iter()
+ .map(|(w, h, pixels)| TrayPixmap {
+ width: w,
+ height: h,
+ pixels,
+ })
+ .collect::<Vec<_>>())
+ }
+ });
+
+ if pixmaps.is_none() {
+ if let Some(ref name) = icon_name {
+ println!("[status-tray] Trying to resolve theme icon for '{}' (theme path: {:?})", name, icon_theme_path);
+ if let Some(icon_path) = resolve_icon_path(icon_theme_path.as_deref(), name) {
+ println!("[status-tray] Found icon file at {:?}", icon_path);
+ if let Some(pixmap) = load_png_as_pixmap(&icon_path) {
+ println!("[status-tray] Successfully decoded icon file to pixmap (size: {}x{})", pixmap.width, pixmap.height);
+ pixmaps = Some(vec![pixmap]);
+ } else {
+ println!("[status-tray] Failed to decode icon file");
+ }
+ } else {
+ println!("[status-tray] Could not find icon file on system or theme path");
+ }
+ }
+ } else {
+ println!("[status-tray] Loaded raw D-Bus pixmap for '{}'", id);
+ }
+
+ Ok(TrayItem {
+ id,
+ icon_name,
+ icon_theme_path,
+ pixmaps,
+ title,
+ })
+}
+
+struct Watcher {
+ registered_items: Arc<tokio::sync::Mutex<HashMap<String, NotifierAddress>>>,
+ proxy_events: EventLoopProxy<CustomEvent>,
+ tokio_handle: tokio::runtime::Handle,
+}
+
+#[zbus::interface(name = "org.kde.StatusNotifierWatcher")]
+impl Watcher {
+ async fn register_status_notifier_item(
+ &self,
+ service: &str,
+ #[zbus(header)] header: zbus::MessageHeader<'_>,
+ #[zbus(connection)] conn: &zbus::Connection,
+ ) {
+ let sender = header
+ .sender()
+ .map(|s| s.to_string())
+ .unwrap_or_else(|| service.to_string());
+
+ if let Ok(addr) = NotifierAddress::from_notifier_service(service, &sender) {
+ let mut items = self.registered_items.lock().await;
+ let full_address = format!("{}/{}", addr.destination, addr.path.trim_start_matches('/'));
+ if !items.contains_key(&full_address) {
+ items.insert(full_address.clone(), addr.clone());
+
+ let conn = conn.clone();
+ let addr_clone = addr.clone();
+ let proxy_events_clone = self.proxy_events.clone();
+
+ self.tokio_handle.spawn(async move {
+ if let Ok(item) = fetch_tray_item(&conn, &addr_clone).await {
+ let _ = proxy_events_clone.send_event(CustomEvent::TrayUpdated(item));
+ }
+
+ // Listen for updates
+ if let Ok(proxy) = StatusNotifierItemProxy::builder(&conn)
+ .destination(addr_clone.destination.clone())
+ .unwrap()
+ .path(addr_clone.path.clone())
+ .unwrap()
+ .build()
+ .await
+ {
+ let mut new_icon_stream = proxy.receive_new_icon().await.ok();
+ let mut new_title_stream = proxy.receive_new_title().await.ok();
+ let mut new_status_stream = proxy.receive_new_status().await.ok();
+
+ use tokio_stream::StreamExt;
+ loop {
+ tokio::select! {
+ Some(_) = async {
+ if let Some(ref mut s) = new_icon_stream {
+ s.next().await
+ } else {
+ std::future::pending().await
+ }
+ } => {
+ if let Ok(item) = fetch_tray_item(&conn, &addr_clone).await {
+ let _ = proxy_events_clone.send_event(CustomEvent::TrayUpdated(item));
+ }
+ }
+ Some(_) = async {
+ if let Some(ref mut s) = new_title_stream {
+ s.next().await
+ } else {
+ std::future::pending().await
+ }
+ } => {
+ if let Ok(item) = fetch_tray_item(&conn, &addr_clone).await {
+ let _ = proxy_events_clone.send_event(CustomEvent::TrayUpdated(item));
+ }
+ }
+ Some(_) = async {
+ if let Some(ref mut s) = new_status_stream {
+ s.next().await
+ } else {
+ std::future::pending().await
+ }
+ } => {
+ if let Ok(item) = fetch_tray_item(&conn, &addr_clone).await {
+ let _ = proxy_events_clone.send_event(CustomEvent::TrayUpdated(item));
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ async fn register_status_notifier_host(&self, _service: &str) {}
+
+ #[zbus(property)]
+ async fn protocol_version(&self) -> i32 {
+ 0
+ }
+
+ #[zbus(property)]
+ async fn is_status_notifier_host_registered(&self) -> bool {
+ true
+ }
+
+ #[zbus(property)]
+ async fn registered_status_notifier_items(&self) -> Vec<String> {
+ let items = self.registered_items.lock().await;
+ items.keys().cloned().collect()
+ }
+}
+
+async fn spawn_status_tray(proxy: EventLoopProxy<CustomEvent>) {
+ let registered_items = Arc::new(tokio::sync::Mutex::new(HashMap::new()));
+ let tokio_handle = tokio::runtime::Handle::current();
+
+ let watcher = Watcher {
+ registered_items: registered_items.clone(),
+ proxy_events: proxy.clone(),
+ tokio_handle,
+ };
+
+ let conn = match zbus::ConnectionBuilder::session() {
+ Ok(builder) => {
+ match builder
+ .name("org.kde.StatusNotifierWatcher")
+ .unwrap()
+ .serve_at("/StatusNotifierWatcher", watcher)
+ .unwrap()
+ .build()
+ .await
+ {
+ Ok(c) => c,
+ Err(e) => {
+ eprintln!("Failed to build D-Bus connection: {:?}", e);
+ return;
+ }
+ }
+ }
Err(e) => {
- eprintln!("Failed to create StatusNotifierWatcher: {:?}", e);
+ eprintln!("Failed to initialize D-Bus session: {:?}", e);
return;
}
};
- let mut host = match watcher.create_notifier_host("clear-status-bar").await {
- Ok(h) => h,
+
+ println!("StatusNotifierWatcher running successfully on D-Bus!");
+
+ // Start NameOwnerChanged listener to detect when tray apps disconnect
+ let dbus_proxy = match zbus::fdo::DBusProxy::new(&conn).await {
+ Ok(p) => p,
Err(e) => {
- eprintln!("Failed to create NotifierHost: {:?}", e);
+ eprintln!("Failed to create DBusProxy: {:?}", e);
return;
}
};
- loop {
- match host.recv().await {
- Ok(msg) => match msg {
- NotifierItemMessage::Update { address, item, menu: _ } => {
- let pixmaps = item.icon_pixmap.map(|v| {
- v.into_iter()
- .map(|p| TrayPixmap {
- width: p.width,
- height: p.height,
- pixels: p.pixels,
- })
- .collect()
- });
- let tray_item = TrayItem {
- id: address.clone(),
- icon_name: item.icon_name,
- icon_theme_path: item.icon_theme_path,
- pixmaps,
- title: item.title,
- };
- let _ = proxy.send_event(CustomEvent::TrayUpdated(tray_item));
- }
- NotifierItemMessage::Remove { address } => {
- let _ = proxy.send_event(CustomEvent::TrayRemoved(address));
+ let mut owner_changes = match dbus_proxy.receive_name_owner_changed().await {
+ Ok(oc) => oc,
+ Err(e) => {
+ eprintln!("Failed to receive name owner changed: {:?}", e);
+ return;
+ }
+ };
+
+ let registered_items_clone = registered_items.clone();
+ let proxy_events_clone = proxy.clone();
+
+ tokio::spawn(async move {
+ use tokio_stream::StreamExt;
+ while let Some(signal) = owner_changes.next().await {
+ if let Ok(args) = signal.args() {
+ let old = args.old_owner;
+ let new = args.new_owner;
+ let old_opt: &Option<_> = &*old;
+ if let Some(ref old_owner) = old_opt {
+ if new.is_none() {
+ let mut items = registered_items_clone.lock().await;
+ let mut to_remove = Vec::new();
+ for (key, addr) in items.iter() {
+ if addr.destination == old_owner.as_str() {
+ to_remove.push(key.clone());
+ }
+ }
+ for key in to_remove {
+ items.remove(&key);
+ let _ = proxy_events_clone.send_event(CustomEvent::TrayRemoved(key));
+ }
+ }
}
- },
- Err(e) => {
- eprintln!("Tray host recv error: {:?}", e);
- tokio::time::sleep(std::time::Duration::from_secs(2)).await;
}
}
+ });
+
+ // Keep the task alive
+ loop {
+ tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
}
}
@@ -972,8 +1444,6 @@ fn main() {
let proxy_stats = event_loop.create_proxy();
let proxy_tray = event_loop.create_proxy();
- let (_cmd_tx, cmd_rx) = tokio::sync::mpsc::channel(32);
-
// Spawn Tokio Runtime for async listeners
std::thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().unwrap();
@@ -982,7 +1452,7 @@ fn main() {
tokio::spawn(spawn_status_listener("layout", proxy_layout));
tokio::spawn(spawn_status_listener("title", proxy_title));
tokio::spawn(spawn_system_stats(proxy_stats));
- tokio::spawn(spawn_status_tray(proxy_tray, cmd_rx));
+ tokio::spawn(spawn_status_tray(proxy_tray));
// Keep the runtime thread alive
loop {
@@ -1013,4 +1483,5 @@ fn main() {
+