├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── gtk-tray ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs └── stray ├── Cargo.toml ├── README.md ├── examples └── simple.rs └── src ├── dbus ├── dbusmenu_proxy.rs ├── mod.rs ├── notifier_item_proxy.rs ├── notifier_watcher_proxy.rs └── notifier_watcher_service.rs ├── error.rs ├── lib.rs ├── message ├── menu.rs ├── mod.rs └── tray.rs ├── notifier_host └── mod.rs └── notifier_watcher ├── mod.rs └── notifier_address.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .buildconfig 3 | .idea -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.57" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" 19 | 20 | [[package]] 21 | name = "async-broadcast" 22 | version = "0.5.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" 25 | dependencies = [ 26 | "event-listener", 27 | "futures-core", 28 | ] 29 | 30 | [[package]] 31 | name = "async-channel" 32 | version = "1.6.1" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 35 | dependencies = [ 36 | "concurrent-queue 1.2.2", 37 | "event-listener", 38 | "futures-core", 39 | ] 40 | 41 | [[package]] 42 | name = "async-io" 43 | version = "1.13.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" 46 | dependencies = [ 47 | "async-lock", 48 | "autocfg", 49 | "cfg-if", 50 | "concurrent-queue 2.2.0", 51 | "futures-lite", 52 | "log", 53 | "parking", 54 | "polling", 55 | "rustix", 56 | "slab", 57 | "socket2", 58 | "waker-fn", 59 | ] 60 | 61 | [[package]] 62 | name = "async-lock" 63 | version = "2.7.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" 66 | dependencies = [ 67 | "event-listener", 68 | ] 69 | 70 | [[package]] 71 | name = "async-process" 72 | version = "1.7.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" 75 | dependencies = [ 76 | "async-io", 77 | "async-lock", 78 | "autocfg", 79 | "blocking", 80 | "cfg-if", 81 | "event-listener", 82 | "futures-lite", 83 | "rustix", 84 | "signal-hook", 85 | "windows-sys 0.48.0", 86 | ] 87 | 88 | [[package]] 89 | name = "async-recursion" 90 | version = "1.0.4" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" 93 | dependencies = [ 94 | "proc-macro2", 95 | "quote", 96 | "syn 2.0.18", 97 | ] 98 | 99 | [[package]] 100 | name = "async-task" 101 | version = "4.2.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" 104 | 105 | [[package]] 106 | name = "async-trait" 107 | version = "0.1.68" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" 110 | dependencies = [ 111 | "proc-macro2", 112 | "quote", 113 | "syn 2.0.18", 114 | ] 115 | 116 | [[package]] 117 | name = "atk" 118 | version = "0.15.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" 121 | dependencies = [ 122 | "atk-sys", 123 | "bitflags", 124 | "glib", 125 | "libc", 126 | ] 127 | 128 | [[package]] 129 | name = "atk-sys" 130 | version = "0.15.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" 133 | dependencies = [ 134 | "glib-sys", 135 | "gobject-sys", 136 | "libc", 137 | "system-deps", 138 | ] 139 | 140 | [[package]] 141 | name = "atomic-waker" 142 | version = "1.1.1" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" 145 | 146 | [[package]] 147 | name = "autocfg" 148 | version = "1.1.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 151 | 152 | [[package]] 153 | name = "bitflags" 154 | version = "1.3.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 157 | 158 | [[package]] 159 | name = "block-buffer" 160 | version = "0.10.4" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 163 | dependencies = [ 164 | "generic-array", 165 | ] 166 | 167 | [[package]] 168 | name = "blocking" 169 | version = "1.3.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" 172 | dependencies = [ 173 | "async-channel", 174 | "async-lock", 175 | "async-task", 176 | "atomic-waker", 177 | "fastrand", 178 | "futures-lite", 179 | "log", 180 | ] 181 | 182 | [[package]] 183 | name = "byteorder" 184 | version = "1.4.3" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 187 | 188 | [[package]] 189 | name = "bytes" 190 | version = "1.1.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 193 | 194 | [[package]] 195 | name = "cache-padded" 196 | version = "1.2.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" 199 | 200 | [[package]] 201 | name = "cairo-rs" 202 | version = "0.15.11" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "62be3562254e90c1c6050a72aa638f6315593e98c5cdaba9017cedbabf0a5dee" 205 | dependencies = [ 206 | "bitflags", 207 | "cairo-sys-rs", 208 | "glib", 209 | "libc", 210 | "thiserror", 211 | ] 212 | 213 | [[package]] 214 | name = "cairo-sys-rs" 215 | version = "0.15.1" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" 218 | dependencies = [ 219 | "glib-sys", 220 | "libc", 221 | "system-deps", 222 | ] 223 | 224 | [[package]] 225 | name = "cc" 226 | version = "1.0.73" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 229 | 230 | [[package]] 231 | name = "cfg-expr" 232 | version = "0.10.2" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "5e068cb2806bbc15b439846dc16c5f89f8599f2c3e4d73d4449d38f9b2f0b6c5" 235 | dependencies = [ 236 | "smallvec", 237 | ] 238 | 239 | [[package]] 240 | name = "cfg-if" 241 | version = "1.0.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 244 | 245 | [[package]] 246 | name = "chrono" 247 | version = "0.4.19" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 250 | dependencies = [ 251 | "libc", 252 | "num-integer", 253 | "num-traits", 254 | "time", 255 | "winapi", 256 | ] 257 | 258 | [[package]] 259 | name = "concurrent-queue" 260 | version = "1.2.2" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 263 | dependencies = [ 264 | "cache-padded", 265 | ] 266 | 267 | [[package]] 268 | name = "concurrent-queue" 269 | version = "2.2.0" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" 272 | dependencies = [ 273 | "crossbeam-utils", 274 | ] 275 | 276 | [[package]] 277 | name = "cpufeatures" 278 | version = "0.2.8" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" 281 | dependencies = [ 282 | "libc", 283 | ] 284 | 285 | [[package]] 286 | name = "crossbeam-utils" 287 | version = "0.8.16" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 290 | dependencies = [ 291 | "cfg-if", 292 | ] 293 | 294 | [[package]] 295 | name = "crypto-common" 296 | version = "0.1.6" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 299 | dependencies = [ 300 | "generic-array", 301 | "typenum", 302 | ] 303 | 304 | [[package]] 305 | name = "derivative" 306 | version = "2.2.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 309 | dependencies = [ 310 | "proc-macro2", 311 | "quote", 312 | "syn 1.0.109", 313 | ] 314 | 315 | [[package]] 316 | name = "digest" 317 | version = "0.10.7" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 320 | dependencies = [ 321 | "block-buffer", 322 | "crypto-common", 323 | ] 324 | 325 | [[package]] 326 | name = "enumflags2" 327 | version = "0.7.5" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" 330 | dependencies = [ 331 | "enumflags2_derive", 332 | "serde", 333 | ] 334 | 335 | [[package]] 336 | name = "enumflags2_derive" 337 | version = "0.7.4" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" 340 | dependencies = [ 341 | "proc-macro2", 342 | "quote", 343 | "syn 1.0.109", 344 | ] 345 | 346 | [[package]] 347 | name = "errno" 348 | version = "0.3.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 351 | dependencies = [ 352 | "errno-dragonfly", 353 | "libc", 354 | "windows-sys 0.48.0", 355 | ] 356 | 357 | [[package]] 358 | name = "errno-dragonfly" 359 | version = "0.1.2" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 362 | dependencies = [ 363 | "cc", 364 | "libc", 365 | ] 366 | 367 | [[package]] 368 | name = "event-listener" 369 | version = "2.5.3" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 372 | 373 | [[package]] 374 | name = "fastrand" 375 | version = "1.7.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 378 | dependencies = [ 379 | "instant", 380 | ] 381 | 382 | [[package]] 383 | name = "field-offset" 384 | version = "0.3.4" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" 387 | dependencies = [ 388 | "memoffset 0.6.5", 389 | "rustc_version", 390 | ] 391 | 392 | [[package]] 393 | name = "futures-channel" 394 | version = "0.3.21" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 397 | dependencies = [ 398 | "futures-core", 399 | ] 400 | 401 | [[package]] 402 | name = "futures-core" 403 | version = "0.3.28" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 406 | 407 | [[package]] 408 | name = "futures-executor" 409 | version = "0.3.21" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" 412 | dependencies = [ 413 | "futures-core", 414 | "futures-task", 415 | "futures-util", 416 | ] 417 | 418 | [[package]] 419 | name = "futures-io" 420 | version = "0.3.21" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 423 | 424 | [[package]] 425 | name = "futures-lite" 426 | version = "1.12.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 429 | dependencies = [ 430 | "fastrand", 431 | "futures-core", 432 | "futures-io", 433 | "memchr", 434 | "parking", 435 | "pin-project-lite", 436 | "waker-fn", 437 | ] 438 | 439 | [[package]] 440 | name = "futures-sink" 441 | version = "0.3.28" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" 444 | 445 | [[package]] 446 | name = "futures-task" 447 | version = "0.3.28" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 450 | 451 | [[package]] 452 | name = "futures-util" 453 | version = "0.3.28" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 456 | dependencies = [ 457 | "futures-core", 458 | "futures-sink", 459 | "futures-task", 460 | "pin-project-lite", 461 | "pin-utils", 462 | "slab", 463 | ] 464 | 465 | [[package]] 466 | name = "gdk" 467 | version = "0.15.4" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" 470 | dependencies = [ 471 | "bitflags", 472 | "cairo-rs", 473 | "gdk-pixbuf", 474 | "gdk-sys", 475 | "gio", 476 | "glib", 477 | "libc", 478 | "pango", 479 | ] 480 | 481 | [[package]] 482 | name = "gdk-pixbuf" 483 | version = "0.15.11" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" 486 | dependencies = [ 487 | "bitflags", 488 | "gdk-pixbuf-sys", 489 | "gio", 490 | "glib", 491 | "libc", 492 | ] 493 | 494 | [[package]] 495 | name = "gdk-pixbuf-sys" 496 | version = "0.15.10" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" 499 | dependencies = [ 500 | "gio-sys", 501 | "glib-sys", 502 | "gobject-sys", 503 | "libc", 504 | "system-deps", 505 | ] 506 | 507 | [[package]] 508 | name = "gdk-sys" 509 | version = "0.15.1" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" 512 | dependencies = [ 513 | "cairo-sys-rs", 514 | "gdk-pixbuf-sys", 515 | "gio-sys", 516 | "glib-sys", 517 | "gobject-sys", 518 | "libc", 519 | "pango-sys", 520 | "pkg-config", 521 | "system-deps", 522 | ] 523 | 524 | [[package]] 525 | name = "generic-array" 526 | version = "0.14.7" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 529 | dependencies = [ 530 | "typenum", 531 | "version_check", 532 | ] 533 | 534 | [[package]] 535 | name = "getrandom" 536 | version = "0.2.6" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" 539 | dependencies = [ 540 | "cfg-if", 541 | "libc", 542 | "wasi 0.10.0+wasi-snapshot-preview1", 543 | ] 544 | 545 | [[package]] 546 | name = "gio" 547 | version = "0.15.11" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "0f132be35e05d9662b9fa0fee3f349c6621f7782e0105917f4cc73c1bf47eceb" 550 | dependencies = [ 551 | "bitflags", 552 | "futures-channel", 553 | "futures-core", 554 | "futures-io", 555 | "gio-sys", 556 | "glib", 557 | "libc", 558 | "once_cell", 559 | "thiserror", 560 | ] 561 | 562 | [[package]] 563 | name = "gio-sys" 564 | version = "0.15.10" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" 567 | dependencies = [ 568 | "glib-sys", 569 | "gobject-sys", 570 | "libc", 571 | "system-deps", 572 | "winapi", 573 | ] 574 | 575 | [[package]] 576 | name = "glib" 577 | version = "0.15.11" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "bd124026a2fa8c33a3d17a3fe59c103f2d9fa5bd92c19e029e037736729abeab" 580 | dependencies = [ 581 | "bitflags", 582 | "futures-channel", 583 | "futures-core", 584 | "futures-executor", 585 | "futures-task", 586 | "glib-macros", 587 | "glib-sys", 588 | "gobject-sys", 589 | "libc", 590 | "once_cell", 591 | "smallvec", 592 | "thiserror", 593 | ] 594 | 595 | [[package]] 596 | name = "glib-macros" 597 | version = "0.15.11" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" 600 | dependencies = [ 601 | "anyhow", 602 | "heck", 603 | "proc-macro-crate", 604 | "proc-macro-error", 605 | "proc-macro2", 606 | "quote", 607 | "syn 1.0.109", 608 | ] 609 | 610 | [[package]] 611 | name = "glib-sys" 612 | version = "0.15.10" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" 615 | dependencies = [ 616 | "libc", 617 | "system-deps", 618 | ] 619 | 620 | [[package]] 621 | name = "gobject-sys" 622 | version = "0.15.10" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" 625 | dependencies = [ 626 | "glib-sys", 627 | "libc", 628 | "system-deps", 629 | ] 630 | 631 | [[package]] 632 | name = "gtk" 633 | version = "0.15.5" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" 636 | dependencies = [ 637 | "atk", 638 | "bitflags", 639 | "cairo-rs", 640 | "field-offset", 641 | "futures-channel", 642 | "gdk", 643 | "gdk-pixbuf", 644 | "gio", 645 | "glib", 646 | "gtk-sys", 647 | "gtk3-macros", 648 | "libc", 649 | "once_cell", 650 | "pango", 651 | "pkg-config", 652 | ] 653 | 654 | [[package]] 655 | name = "gtk-sys" 656 | version = "0.15.3" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" 659 | dependencies = [ 660 | "atk-sys", 661 | "cairo-sys-rs", 662 | "gdk-pixbuf-sys", 663 | "gdk-sys", 664 | "gio-sys", 665 | "glib-sys", 666 | "gobject-sys", 667 | "libc", 668 | "pango-sys", 669 | "system-deps", 670 | ] 671 | 672 | [[package]] 673 | name = "gtk-tray" 674 | version = "0.1.0" 675 | dependencies = [ 676 | "gtk", 677 | "once_cell", 678 | "stray", 679 | "tokio", 680 | "tracing-subscriber", 681 | ] 682 | 683 | [[package]] 684 | name = "gtk3-macros" 685 | version = "0.15.4" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" 688 | dependencies = [ 689 | "anyhow", 690 | "proc-macro-crate", 691 | "proc-macro-error", 692 | "proc-macro2", 693 | "quote", 694 | "syn 1.0.109", 695 | ] 696 | 697 | [[package]] 698 | name = "hashbrown" 699 | version = "0.12.3" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 702 | 703 | [[package]] 704 | name = "heck" 705 | version = "0.4.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 708 | 709 | [[package]] 710 | name = "hermit-abi" 711 | version = "0.1.19" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 714 | dependencies = [ 715 | "libc", 716 | ] 717 | 718 | [[package]] 719 | name = "hermit-abi" 720 | version = "0.3.1" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 723 | 724 | [[package]] 725 | name = "hex" 726 | version = "0.4.3" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 729 | 730 | [[package]] 731 | name = "indexmap" 732 | version = "1.9.3" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 735 | dependencies = [ 736 | "autocfg", 737 | "hashbrown", 738 | ] 739 | 740 | [[package]] 741 | name = "instant" 742 | version = "0.1.12" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 745 | dependencies = [ 746 | "cfg-if", 747 | ] 748 | 749 | [[package]] 750 | name = "io-lifetimes" 751 | version = "1.0.11" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 754 | dependencies = [ 755 | "hermit-abi 0.3.1", 756 | "libc", 757 | "windows-sys 0.48.0", 758 | ] 759 | 760 | [[package]] 761 | name = "lazy_static" 762 | version = "1.4.0" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 765 | 766 | [[package]] 767 | name = "libc" 768 | version = "0.2.146" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" 771 | 772 | [[package]] 773 | name = "linux-raw-sys" 774 | version = "0.3.8" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 777 | 778 | [[package]] 779 | name = "lock_api" 780 | version = "0.4.7" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 783 | dependencies = [ 784 | "autocfg", 785 | "scopeguard", 786 | ] 787 | 788 | [[package]] 789 | name = "log" 790 | version = "0.4.17" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 793 | dependencies = [ 794 | "cfg-if", 795 | ] 796 | 797 | [[package]] 798 | name = "memchr" 799 | version = "2.4.1" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 802 | 803 | [[package]] 804 | name = "memoffset" 805 | version = "0.6.5" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 808 | dependencies = [ 809 | "autocfg", 810 | ] 811 | 812 | [[package]] 813 | name = "memoffset" 814 | version = "0.7.1" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 817 | dependencies = [ 818 | "autocfg", 819 | ] 820 | 821 | [[package]] 822 | name = "mio" 823 | version = "0.8.8" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" 826 | dependencies = [ 827 | "libc", 828 | "log", 829 | "wasi 0.11.0+wasi-snapshot-preview1", 830 | "windows-sys 0.48.0", 831 | ] 832 | 833 | [[package]] 834 | name = "nix" 835 | version = "0.26.2" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" 838 | dependencies = [ 839 | "bitflags", 840 | "cfg-if", 841 | "libc", 842 | "memoffset 0.7.1", 843 | "static_assertions", 844 | ] 845 | 846 | [[package]] 847 | name = "nu-ansi-term" 848 | version = "0.46.0" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 851 | dependencies = [ 852 | "overload", 853 | "winapi", 854 | ] 855 | 856 | [[package]] 857 | name = "num-integer" 858 | version = "0.1.44" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 861 | dependencies = [ 862 | "autocfg", 863 | "num-traits", 864 | ] 865 | 866 | [[package]] 867 | name = "num-traits" 868 | version = "0.2.14" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 871 | dependencies = [ 872 | "autocfg", 873 | ] 874 | 875 | [[package]] 876 | name = "num_cpus" 877 | version = "1.13.1" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 880 | dependencies = [ 881 | "hermit-abi 0.1.19", 882 | "libc", 883 | ] 884 | 885 | [[package]] 886 | name = "once_cell" 887 | version = "1.15.0" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 890 | 891 | [[package]] 892 | name = "ordered-stream" 893 | version = "0.2.0" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" 896 | dependencies = [ 897 | "futures-core", 898 | "pin-project-lite", 899 | ] 900 | 901 | [[package]] 902 | name = "overload" 903 | version = "0.1.1" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 906 | 907 | [[package]] 908 | name = "pango" 909 | version = "0.15.10" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" 912 | dependencies = [ 913 | "bitflags", 914 | "glib", 915 | "libc", 916 | "once_cell", 917 | "pango-sys", 918 | ] 919 | 920 | [[package]] 921 | name = "pango-sys" 922 | version = "0.15.10" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" 925 | dependencies = [ 926 | "glib-sys", 927 | "gobject-sys", 928 | "libc", 929 | "system-deps", 930 | ] 931 | 932 | [[package]] 933 | name = "parking" 934 | version = "2.0.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 937 | 938 | [[package]] 939 | name = "parking_lot" 940 | version = "0.12.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 943 | dependencies = [ 944 | "lock_api", 945 | "parking_lot_core", 946 | ] 947 | 948 | [[package]] 949 | name = "parking_lot_core" 950 | version = "0.9.2" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" 953 | dependencies = [ 954 | "cfg-if", 955 | "libc", 956 | "redox_syscall 0.2.13", 957 | "smallvec", 958 | "windows-sys 0.34.0", 959 | ] 960 | 961 | [[package]] 962 | name = "pest" 963 | version = "2.1.3" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 966 | dependencies = [ 967 | "ucd-trie", 968 | ] 969 | 970 | [[package]] 971 | name = "pin-project-lite" 972 | version = "0.2.9" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 975 | 976 | [[package]] 977 | name = "pin-utils" 978 | version = "0.1.0" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 981 | 982 | [[package]] 983 | name = "pkg-config" 984 | version = "0.3.25" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 987 | 988 | [[package]] 989 | name = "polling" 990 | version = "2.8.0" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" 993 | dependencies = [ 994 | "autocfg", 995 | "bitflags", 996 | "cfg-if", 997 | "concurrent-queue 2.2.0", 998 | "libc", 999 | "log", 1000 | "pin-project-lite", 1001 | "windows-sys 0.48.0", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "ppv-lite86" 1006 | version = "0.2.16" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 1009 | 1010 | [[package]] 1011 | name = "proc-macro-crate" 1012 | version = "1.3.1" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 1015 | dependencies = [ 1016 | "once_cell", 1017 | "toml_edit", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "proc-macro-error" 1022 | version = "1.0.4" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1025 | dependencies = [ 1026 | "proc-macro-error-attr", 1027 | "proc-macro2", 1028 | "quote", 1029 | "syn 1.0.109", 1030 | "version_check", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "proc-macro-error-attr" 1035 | version = "1.0.4" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1038 | dependencies = [ 1039 | "proc-macro2", 1040 | "quote", 1041 | "version_check", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "proc-macro2" 1046 | version = "1.0.60" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 1049 | dependencies = [ 1050 | "unicode-ident", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "quote" 1055 | version = "1.0.28" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" 1058 | dependencies = [ 1059 | "proc-macro2", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "rand" 1064 | version = "0.8.5" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1067 | dependencies = [ 1068 | "libc", 1069 | "rand_chacha", 1070 | "rand_core", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "rand_chacha" 1075 | version = "0.3.1" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1078 | dependencies = [ 1079 | "ppv-lite86", 1080 | "rand_core", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "rand_core" 1085 | version = "0.6.3" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1088 | dependencies = [ 1089 | "getrandom", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "redox_syscall" 1094 | version = "0.2.13" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 1097 | dependencies = [ 1098 | "bitflags", 1099 | ] 1100 | 1101 | [[package]] 1102 | name = "redox_syscall" 1103 | version = "0.3.5" 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" 1105 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 1106 | dependencies = [ 1107 | "bitflags", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "regex" 1112 | version = "1.7.3" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" 1115 | dependencies = [ 1116 | "aho-corasick", 1117 | "memchr", 1118 | "regex-syntax", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "regex-syntax" 1123 | version = "0.6.29" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1126 | 1127 | [[package]] 1128 | name = "rustc_version" 1129 | version = "0.3.3" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" 1132 | dependencies = [ 1133 | "semver", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "rustix" 1138 | version = "0.37.20" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" 1141 | dependencies = [ 1142 | "bitflags", 1143 | "errno", 1144 | "io-lifetimes", 1145 | "libc", 1146 | "linux-raw-sys", 1147 | "windows-sys 0.48.0", 1148 | ] 1149 | 1150 | [[package]] 1151 | name = "scopeguard" 1152 | version = "1.1.0" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1155 | 1156 | [[package]] 1157 | name = "semver" 1158 | version = "0.11.0" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 1161 | dependencies = [ 1162 | "semver-parser", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "semver-parser" 1167 | version = "0.10.2" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 1170 | dependencies = [ 1171 | "pest", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "serde" 1176 | version = "1.0.136" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 1179 | dependencies = [ 1180 | "serde_derive", 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "serde_derive" 1185 | version = "1.0.136" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 1188 | dependencies = [ 1189 | "proc-macro2", 1190 | "quote", 1191 | "syn 1.0.109", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "serde_repr" 1196 | version = "0.1.12" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" 1199 | dependencies = [ 1200 | "proc-macro2", 1201 | "quote", 1202 | "syn 2.0.18", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "sha1" 1207 | version = "0.10.5" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1210 | dependencies = [ 1211 | "cfg-if", 1212 | "cpufeatures", 1213 | "digest", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "sharded-slab" 1218 | version = "0.1.4" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 1221 | dependencies = [ 1222 | "lazy_static", 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "signal-hook" 1227 | version = "0.3.15" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" 1230 | dependencies = [ 1231 | "libc", 1232 | "signal-hook-registry", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "signal-hook-registry" 1237 | version = "1.4.0" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1240 | dependencies = [ 1241 | "libc", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "slab" 1246 | version = "0.4.6" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" 1249 | 1250 | [[package]] 1251 | name = "smallvec" 1252 | version = "1.10.0" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1255 | 1256 | [[package]] 1257 | name = "socket2" 1258 | version = "0.4.4" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 1261 | dependencies = [ 1262 | "libc", 1263 | "winapi", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "static_assertions" 1268 | version = "1.1.0" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1271 | 1272 | [[package]] 1273 | name = "stray" 1274 | version = "0.1.3" 1275 | dependencies = [ 1276 | "anyhow", 1277 | "byteorder", 1278 | "chrono", 1279 | "log", 1280 | "serde", 1281 | "thiserror", 1282 | "tokio", 1283 | "tokio-stream", 1284 | "tracing", 1285 | "zbus", 1286 | ] 1287 | 1288 | [[package]] 1289 | name = "syn" 1290 | version = "1.0.109" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1293 | dependencies = [ 1294 | "proc-macro2", 1295 | "quote", 1296 | "unicode-ident", 1297 | ] 1298 | 1299 | [[package]] 1300 | name = "syn" 1301 | version = "2.0.18" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" 1304 | dependencies = [ 1305 | "proc-macro2", 1306 | "quote", 1307 | "unicode-ident", 1308 | ] 1309 | 1310 | [[package]] 1311 | name = "system-deps" 1312 | version = "6.0.2" 1313 | source = "registry+https://github.com/rust-lang/crates.io-index" 1314 | checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709" 1315 | dependencies = [ 1316 | "cfg-expr", 1317 | "heck", 1318 | "pkg-config", 1319 | "toml", 1320 | "version-compare", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "tempfile" 1325 | version = "3.6.0" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" 1328 | dependencies = [ 1329 | "autocfg", 1330 | "cfg-if", 1331 | "fastrand", 1332 | "redox_syscall 0.3.5", 1333 | "rustix", 1334 | "windows-sys 0.48.0", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "thiserror" 1339 | version = "1.0.31" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" 1342 | dependencies = [ 1343 | "thiserror-impl", 1344 | ] 1345 | 1346 | [[package]] 1347 | name = "thiserror-impl" 1348 | version = "1.0.31" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" 1351 | dependencies = [ 1352 | "proc-macro2", 1353 | "quote", 1354 | "syn 1.0.109", 1355 | ] 1356 | 1357 | [[package]] 1358 | name = "thread_local" 1359 | version = "1.1.4" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" 1362 | dependencies = [ 1363 | "once_cell", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "time" 1368 | version = "0.1.44" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1371 | dependencies = [ 1372 | "libc", 1373 | "wasi 0.10.0+wasi-snapshot-preview1", 1374 | "winapi", 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "tokio" 1379 | version = "1.26.0" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" 1382 | dependencies = [ 1383 | "autocfg", 1384 | "bytes", 1385 | "libc", 1386 | "memchr", 1387 | "mio", 1388 | "num_cpus", 1389 | "parking_lot", 1390 | "pin-project-lite", 1391 | "signal-hook-registry", 1392 | "socket2", 1393 | "tokio-macros", 1394 | "tracing", 1395 | "windows-sys 0.45.0", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "tokio-macros" 1400 | version = "1.7.0" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 1403 | dependencies = [ 1404 | "proc-macro2", 1405 | "quote", 1406 | "syn 1.0.109", 1407 | ] 1408 | 1409 | [[package]] 1410 | name = "tokio-stream" 1411 | version = "0.1.8" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" 1414 | dependencies = [ 1415 | "futures-core", 1416 | "pin-project-lite", 1417 | "tokio", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "toml" 1422 | version = "0.5.9" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1425 | dependencies = [ 1426 | "serde", 1427 | ] 1428 | 1429 | [[package]] 1430 | name = "toml_datetime" 1431 | version = "0.6.2" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" 1434 | 1435 | [[package]] 1436 | name = "toml_edit" 1437 | version = "0.19.8" 1438 | source = "registry+https://github.com/rust-lang/crates.io-index" 1439 | checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" 1440 | dependencies = [ 1441 | "indexmap", 1442 | "toml_datetime", 1443 | "winnow", 1444 | ] 1445 | 1446 | [[package]] 1447 | name = "tracing" 1448 | version = "0.1.37" 1449 | source = "registry+https://github.com/rust-lang/crates.io-index" 1450 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1451 | dependencies = [ 1452 | "cfg-if", 1453 | "pin-project-lite", 1454 | "tracing-attributes", 1455 | "tracing-core", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "tracing-attributes" 1460 | version = "0.1.24" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" 1463 | dependencies = [ 1464 | "proc-macro2", 1465 | "quote", 1466 | "syn 2.0.18", 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "tracing-core" 1471 | version = "0.1.30" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1474 | dependencies = [ 1475 | "once_cell", 1476 | "valuable", 1477 | ] 1478 | 1479 | [[package]] 1480 | name = "tracing-log" 1481 | version = "0.1.3" 1482 | source = "registry+https://github.com/rust-lang/crates.io-index" 1483 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" 1484 | dependencies = [ 1485 | "lazy_static", 1486 | "log", 1487 | "tracing-core", 1488 | ] 1489 | 1490 | [[package]] 1491 | name = "tracing-subscriber" 1492 | version = "0.3.16" 1493 | source = "registry+https://github.com/rust-lang/crates.io-index" 1494 | checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" 1495 | dependencies = [ 1496 | "nu-ansi-term", 1497 | "sharded-slab", 1498 | "smallvec", 1499 | "thread_local", 1500 | "tracing-core", 1501 | "tracing-log", 1502 | ] 1503 | 1504 | [[package]] 1505 | name = "typenum" 1506 | version = "1.16.0" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 1509 | 1510 | [[package]] 1511 | name = "ucd-trie" 1512 | version = "0.1.3" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1515 | 1516 | [[package]] 1517 | name = "uds_windows" 1518 | version = "1.0.2" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" 1521 | dependencies = [ 1522 | "tempfile", 1523 | "winapi", 1524 | ] 1525 | 1526 | [[package]] 1527 | name = "unicode-ident" 1528 | version = "1.0.9" 1529 | source = "registry+https://github.com/rust-lang/crates.io-index" 1530 | checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" 1531 | 1532 | [[package]] 1533 | name = "valuable" 1534 | version = "0.1.0" 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" 1536 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1537 | 1538 | [[package]] 1539 | name = "version-compare" 1540 | version = "0.1.0" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" 1543 | 1544 | [[package]] 1545 | name = "version_check" 1546 | version = "0.9.4" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1549 | 1550 | [[package]] 1551 | name = "waker-fn" 1552 | version = "1.1.0" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 1555 | 1556 | [[package]] 1557 | name = "wasi" 1558 | version = "0.10.0+wasi-snapshot-preview1" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1561 | 1562 | [[package]] 1563 | name = "wasi" 1564 | version = "0.11.0+wasi-snapshot-preview1" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1567 | 1568 | [[package]] 1569 | name = "winapi" 1570 | version = "0.3.9" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1573 | dependencies = [ 1574 | "winapi-i686-pc-windows-gnu", 1575 | "winapi-x86_64-pc-windows-gnu", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "winapi-i686-pc-windows-gnu" 1580 | version = "0.4.0" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1583 | 1584 | [[package]] 1585 | name = "winapi-x86_64-pc-windows-gnu" 1586 | version = "0.4.0" 1587 | source = "registry+https://github.com/rust-lang/crates.io-index" 1588 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1589 | 1590 | [[package]] 1591 | name = "windows-sys" 1592 | version = "0.34.0" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" 1595 | dependencies = [ 1596 | "windows_aarch64_msvc 0.34.0", 1597 | "windows_i686_gnu 0.34.0", 1598 | "windows_i686_msvc 0.34.0", 1599 | "windows_x86_64_gnu 0.34.0", 1600 | "windows_x86_64_msvc 0.34.0", 1601 | ] 1602 | 1603 | [[package]] 1604 | name = "windows-sys" 1605 | version = "0.45.0" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1608 | dependencies = [ 1609 | "windows-targets 0.42.2", 1610 | ] 1611 | 1612 | [[package]] 1613 | name = "windows-sys" 1614 | version = "0.48.0" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1617 | dependencies = [ 1618 | "windows-targets 0.48.0", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "windows-targets" 1623 | version = "0.42.2" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1626 | dependencies = [ 1627 | "windows_aarch64_gnullvm 0.42.2", 1628 | "windows_aarch64_msvc 0.42.2", 1629 | "windows_i686_gnu 0.42.2", 1630 | "windows_i686_msvc 0.42.2", 1631 | "windows_x86_64_gnu 0.42.2", 1632 | "windows_x86_64_gnullvm 0.42.2", 1633 | "windows_x86_64_msvc 0.42.2", 1634 | ] 1635 | 1636 | [[package]] 1637 | name = "windows-targets" 1638 | version = "0.48.0" 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" 1640 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 1641 | dependencies = [ 1642 | "windows_aarch64_gnullvm 0.48.0", 1643 | "windows_aarch64_msvc 0.48.0", 1644 | "windows_i686_gnu 0.48.0", 1645 | "windows_i686_msvc 0.48.0", 1646 | "windows_x86_64_gnu 0.48.0", 1647 | "windows_x86_64_gnullvm 0.48.0", 1648 | "windows_x86_64_msvc 0.48.0", 1649 | ] 1650 | 1651 | [[package]] 1652 | name = "windows_aarch64_gnullvm" 1653 | version = "0.42.2" 1654 | source = "registry+https://github.com/rust-lang/crates.io-index" 1655 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 1656 | 1657 | [[package]] 1658 | name = "windows_aarch64_gnullvm" 1659 | version = "0.48.0" 1660 | source = "registry+https://github.com/rust-lang/crates.io-index" 1661 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 1662 | 1663 | [[package]] 1664 | name = "windows_aarch64_msvc" 1665 | version = "0.34.0" 1666 | source = "registry+https://github.com/rust-lang/crates.io-index" 1667 | checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" 1668 | 1669 | [[package]] 1670 | name = "windows_aarch64_msvc" 1671 | version = "0.42.2" 1672 | source = "registry+https://github.com/rust-lang/crates.io-index" 1673 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 1674 | 1675 | [[package]] 1676 | name = "windows_aarch64_msvc" 1677 | version = "0.48.0" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 1680 | 1681 | [[package]] 1682 | name = "windows_i686_gnu" 1683 | version = "0.34.0" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" 1686 | 1687 | [[package]] 1688 | name = "windows_i686_gnu" 1689 | version = "0.42.2" 1690 | source = "registry+https://github.com/rust-lang/crates.io-index" 1691 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 1692 | 1693 | [[package]] 1694 | name = "windows_i686_gnu" 1695 | version = "0.48.0" 1696 | source = "registry+https://github.com/rust-lang/crates.io-index" 1697 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 1698 | 1699 | [[package]] 1700 | name = "windows_i686_msvc" 1701 | version = "0.34.0" 1702 | source = "registry+https://github.com/rust-lang/crates.io-index" 1703 | checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" 1704 | 1705 | [[package]] 1706 | name = "windows_i686_msvc" 1707 | version = "0.42.2" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 1710 | 1711 | [[package]] 1712 | name = "windows_i686_msvc" 1713 | version = "0.48.0" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 1716 | 1717 | [[package]] 1718 | name = "windows_x86_64_gnu" 1719 | version = "0.34.0" 1720 | source = "registry+https://github.com/rust-lang/crates.io-index" 1721 | checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" 1722 | 1723 | [[package]] 1724 | name = "windows_x86_64_gnu" 1725 | version = "0.42.2" 1726 | source = "registry+https://github.com/rust-lang/crates.io-index" 1727 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 1728 | 1729 | [[package]] 1730 | name = "windows_x86_64_gnu" 1731 | version = "0.48.0" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 1734 | 1735 | [[package]] 1736 | name = "windows_x86_64_gnullvm" 1737 | version = "0.42.2" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 1740 | 1741 | [[package]] 1742 | name = "windows_x86_64_gnullvm" 1743 | version = "0.48.0" 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" 1745 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1746 | 1747 | [[package]] 1748 | name = "windows_x86_64_msvc" 1749 | version = "0.34.0" 1750 | source = "registry+https://github.com/rust-lang/crates.io-index" 1751 | checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" 1752 | 1753 | [[package]] 1754 | name = "windows_x86_64_msvc" 1755 | version = "0.42.2" 1756 | source = "registry+https://github.com/rust-lang/crates.io-index" 1757 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 1758 | 1759 | [[package]] 1760 | name = "windows_x86_64_msvc" 1761 | version = "0.48.0" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1764 | 1765 | [[package]] 1766 | name = "winnow" 1767 | version = "0.4.1" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" 1770 | dependencies = [ 1771 | "memchr", 1772 | ] 1773 | 1774 | [[package]] 1775 | name = "xdg-home" 1776 | version = "1.0.0" 1777 | source = "registry+https://github.com/rust-lang/crates.io-index" 1778 | checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" 1779 | dependencies = [ 1780 | "nix", 1781 | "winapi", 1782 | ] 1783 | 1784 | [[package]] 1785 | name = "zbus" 1786 | version = "3.13.1" 1787 | source = "registry+https://github.com/rust-lang/crates.io-index" 1788 | checksum = "6c3d77c9966c28321f1907f0b6c5a5561189d1f7311eea6d94180c6be9daab29" 1789 | dependencies = [ 1790 | "async-broadcast", 1791 | "async-process", 1792 | "async-recursion", 1793 | "async-trait", 1794 | "byteorder", 1795 | "derivative", 1796 | "enumflags2", 1797 | "event-listener", 1798 | "futures-core", 1799 | "futures-sink", 1800 | "futures-util", 1801 | "hex", 1802 | "nix", 1803 | "once_cell", 1804 | "ordered-stream", 1805 | "rand", 1806 | "serde", 1807 | "serde_repr", 1808 | "sha1", 1809 | "static_assertions", 1810 | "tokio", 1811 | "tracing", 1812 | "uds_windows", 1813 | "winapi", 1814 | "xdg-home", 1815 | "zbus_macros", 1816 | "zbus_names", 1817 | "zvariant", 1818 | ] 1819 | 1820 | [[package]] 1821 | name = "zbus_macros" 1822 | version = "3.13.1" 1823 | source = "registry+https://github.com/rust-lang/crates.io-index" 1824 | checksum = "f6e341d12edaff644e539ccbbf7f161601294c9a84ed3d7e015da33155b435af" 1825 | dependencies = [ 1826 | "proc-macro-crate", 1827 | "proc-macro2", 1828 | "quote", 1829 | "regex", 1830 | "syn 1.0.109", 1831 | "winnow", 1832 | "zvariant_utils", 1833 | ] 1834 | 1835 | [[package]] 1836 | name = "zbus_names" 1837 | version = "2.5.1" 1838 | source = "registry+https://github.com/rust-lang/crates.io-index" 1839 | checksum = "82441e6033be0a741157a72951a3e4957d519698f3a824439cc131c5ba77ac2a" 1840 | dependencies = [ 1841 | "serde", 1842 | "static_assertions", 1843 | "zvariant", 1844 | ] 1845 | 1846 | [[package]] 1847 | name = "zvariant" 1848 | version = "3.14.0" 1849 | source = "registry+https://github.com/rust-lang/crates.io-index" 1850 | checksum = "622cc473f10cef1b0d73b7b34a266be30ebdcfaea40ec297dd8cbda088f9f93c" 1851 | dependencies = [ 1852 | "byteorder", 1853 | "enumflags2", 1854 | "libc", 1855 | "serde", 1856 | "static_assertions", 1857 | "zvariant_derive", 1858 | ] 1859 | 1860 | [[package]] 1861 | name = "zvariant_derive" 1862 | version = "3.14.0" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "5d9c1b57352c25b778257c661f3c4744b7cefb7fc09dd46909a153cce7773da2" 1865 | dependencies = [ 1866 | "proc-macro-crate", 1867 | "proc-macro2", 1868 | "quote", 1869 | "syn 1.0.109", 1870 | "zvariant_utils", 1871 | ] 1872 | 1873 | [[package]] 1874 | name = "zvariant_utils" 1875 | version = "1.0.1" 1876 | source = "registry+https://github.com/rust-lang/crates.io-index" 1877 | checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" 1878 | dependencies = [ 1879 | "proc-macro2", 1880 | "quote", 1881 | "syn 1.0.109", 1882 | ] 1883 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "stray", 5 | "gtk-tray" 6 | ] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stray 2 | 3 | Stray is a [SystemNotifierWatcher](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/) 4 | implementation which goal is to provide a minimalistic API to access tray icons and menu. 5 | 6 | ## Examples 7 | 8 | ### Start the system tray and listen for changes 9 | ```rust, ignore 10 | use stray::{SystemTray}; 11 | use tokio_stream::StreamExt; 12 | use stray::message::NotifierItemMessage; 13 | use stray::message::NotifierItemCommand; 14 | 15 | #[tokio::main] 16 | async fn main() { 17 | 18 | // A mpsc channel to send menu activation requests later 19 | let (ui_tx, ui_rx) = tokio::sync::mpsc::channel(32); 20 | let mut tray = SystemTray::new(ui_rx).await; 21 | 22 | while let Some(message) = tray.next().await { 23 | match message { 24 | NotifierItemMessage::Update { address: id, item, menu } => { 25 | println!("NotifierItem updated : 26 | id = {id}, 27 | item = {item:?}, 28 | menu = {menu:?}" 29 | ) 30 | } 31 | NotifierItemMessage::Remove { address: id } => { 32 | println!("NotifierItem removed : id = {id}"); 33 | } 34 | } 35 | } 36 | } 37 | ``` 38 | 39 | ### Send menu activation request to the system tray 40 | 41 | ```rust, ignore 42 | // Assuming we stored our menu items in some UI state we can send menu item activation request: 43 | use stray::message::NotifierItemCommand; 44 | 45 | ui_tx.clone().try_send(NotifierItemCommand::MenuItemClicked { 46 | // The submenu to activate 47 | submenu_id: 32, 48 | // dbus menu path, available in the `StatusNotifierItem` 49 | menu_path: "/org/ayatana/NotificationItem/Element1/Menu".to_string(), 50 | // the notifier address we previously got from `NotifierItemMessage::Update` 51 | notifier_address: ":1.2161".to_string(), 52 | }).unwrap(); 53 | ``` 54 | 55 | ### Gtk example 56 | 57 | For a detailed, real life example, you can take a look at the [gtk-tray](https://github.com/oknozor/stray/tree/main/gtk-tray). 58 | 59 | ```shell 60 | git clone git@github.com:oknozor/stray.git 61 | cd stray/gtk-tray 62 | cargo run 63 | ``` 64 | -------------------------------------------------------------------------------- /gtk-tray/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /gtk-tray/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.57" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" 19 | 20 | [[package]] 21 | name = "async-broadcast" 22 | version = "0.3.4" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b" 25 | dependencies = [ 26 | "easy-parallel", 27 | "event-listener", 28 | "futures-core", 29 | ] 30 | 31 | [[package]] 32 | name = "async-channel" 33 | version = "1.6.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 36 | dependencies = [ 37 | "concurrent-queue", 38 | "event-listener", 39 | "futures-core", 40 | ] 41 | 42 | [[package]] 43 | name = "async-executor" 44 | version = "1.4.1" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 47 | dependencies = [ 48 | "async-task", 49 | "concurrent-queue", 50 | "fastrand", 51 | "futures-lite", 52 | "once_cell", 53 | "slab", 54 | ] 55 | 56 | [[package]] 57 | name = "async-lock" 58 | version = "2.5.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" 61 | dependencies = [ 62 | "event-listener", 63 | ] 64 | 65 | [[package]] 66 | name = "async-recursion" 67 | version = "0.3.2" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" 70 | dependencies = [ 71 | "proc-macro2", 72 | "quote", 73 | "syn", 74 | ] 75 | 76 | [[package]] 77 | name = "async-task" 78 | version = "4.2.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" 81 | 82 | [[package]] 83 | name = "async-trait" 84 | version = "0.1.53" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" 87 | dependencies = [ 88 | "proc-macro2", 89 | "quote", 90 | "syn", 91 | ] 92 | 93 | [[package]] 94 | name = "atk" 95 | version = "0.15.1" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" 98 | dependencies = [ 99 | "atk-sys", 100 | "bitflags", 101 | "glib", 102 | "libc", 103 | ] 104 | 105 | [[package]] 106 | name = "atk-sys" 107 | version = "0.15.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" 110 | dependencies = [ 111 | "glib-sys", 112 | "gobject-sys", 113 | "libc", 114 | "system-deps", 115 | ] 116 | 117 | [[package]] 118 | name = "autocfg" 119 | version = "1.1.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 122 | 123 | [[package]] 124 | name = "bitflags" 125 | version = "1.3.2" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 128 | 129 | [[package]] 130 | name = "byteorder" 131 | version = "1.4.3" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 134 | 135 | [[package]] 136 | name = "bytes" 137 | version = "1.1.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 140 | 141 | [[package]] 142 | name = "cache-padded" 143 | version = "1.2.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" 146 | 147 | [[package]] 148 | name = "cairo-rs" 149 | version = "0.15.10" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "129e928d3eda625f53ce257589efbe5143416875fd01bddd08c8c6feb8b9962b" 152 | dependencies = [ 153 | "bitflags", 154 | "cairo-sys-rs", 155 | "glib", 156 | "libc", 157 | "thiserror", 158 | ] 159 | 160 | [[package]] 161 | name = "cairo-sys-rs" 162 | version = "0.15.1" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" 165 | dependencies = [ 166 | "glib-sys", 167 | "libc", 168 | "system-deps", 169 | ] 170 | 171 | [[package]] 172 | name = "cc" 173 | version = "1.0.73" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 176 | 177 | [[package]] 178 | name = "cfg-expr" 179 | version = "0.10.2" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "5e068cb2806bbc15b439846dc16c5f89f8599f2c3e4d73d4449d38f9b2f0b6c5" 182 | dependencies = [ 183 | "smallvec", 184 | ] 185 | 186 | [[package]] 187 | name = "cfg-if" 188 | version = "1.0.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 191 | 192 | [[package]] 193 | name = "concurrent-queue" 194 | version = "1.2.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 197 | dependencies = [ 198 | "cache-padded", 199 | ] 200 | 201 | [[package]] 202 | name = "derivative" 203 | version = "2.2.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 206 | dependencies = [ 207 | "proc-macro2", 208 | "quote", 209 | "syn", 210 | ] 211 | 212 | [[package]] 213 | name = "easy-parallel" 214 | version = "3.2.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" 217 | 218 | [[package]] 219 | name = "enumflags2" 220 | version = "0.7.5" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" 223 | dependencies = [ 224 | "enumflags2_derive", 225 | "serde", 226 | ] 227 | 228 | [[package]] 229 | name = "enumflags2_derive" 230 | version = "0.7.4" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" 233 | dependencies = [ 234 | "proc-macro2", 235 | "quote", 236 | "syn", 237 | ] 238 | 239 | [[package]] 240 | name = "event-listener" 241 | version = "2.5.2" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" 244 | 245 | [[package]] 246 | name = "fastrand" 247 | version = "1.7.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 250 | dependencies = [ 251 | "instant", 252 | ] 253 | 254 | [[package]] 255 | name = "field-offset" 256 | version = "0.3.4" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" 259 | dependencies = [ 260 | "memoffset", 261 | "rustc_version", 262 | ] 263 | 264 | [[package]] 265 | name = "futures-channel" 266 | version = "0.3.21" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 269 | dependencies = [ 270 | "futures-core", 271 | ] 272 | 273 | [[package]] 274 | name = "futures-core" 275 | version = "0.3.21" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 278 | 279 | [[package]] 280 | name = "futures-executor" 281 | version = "0.3.21" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" 284 | dependencies = [ 285 | "futures-core", 286 | "futures-task", 287 | "futures-util", 288 | ] 289 | 290 | [[package]] 291 | name = "futures-io" 292 | version = "0.3.21" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 295 | 296 | [[package]] 297 | name = "futures-lite" 298 | version = "1.12.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 301 | dependencies = [ 302 | "fastrand", 303 | "futures-core", 304 | "futures-io", 305 | "memchr", 306 | "parking", 307 | "pin-project-lite", 308 | "waker-fn", 309 | ] 310 | 311 | [[package]] 312 | name = "futures-sink" 313 | version = "0.3.21" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 316 | 317 | [[package]] 318 | name = "futures-task" 319 | version = "0.3.21" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 322 | 323 | [[package]] 324 | name = "futures-util" 325 | version = "0.3.21" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 328 | dependencies = [ 329 | "futures-core", 330 | "futures-sink", 331 | "futures-task", 332 | "pin-project-lite", 333 | "pin-utils", 334 | "slab", 335 | ] 336 | 337 | [[package]] 338 | name = "gdk" 339 | version = "0.15.4" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" 342 | dependencies = [ 343 | "bitflags", 344 | "cairo-rs", 345 | "gdk-pixbuf", 346 | "gdk-sys", 347 | "gio", 348 | "glib", 349 | "libc", 350 | "pango", 351 | ] 352 | 353 | [[package]] 354 | name = "gdk-pixbuf" 355 | version = "0.15.10" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "678516f1baef591d270ca10587c01a12542a731a7879cc62391a18191a470831" 358 | dependencies = [ 359 | "bitflags", 360 | "gdk-pixbuf-sys", 361 | "gio", 362 | "glib", 363 | "libc", 364 | ] 365 | 366 | [[package]] 367 | name = "gdk-pixbuf-sys" 368 | version = "0.15.10" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" 371 | dependencies = [ 372 | "gio-sys", 373 | "glib-sys", 374 | "gobject-sys", 375 | "libc", 376 | "system-deps", 377 | ] 378 | 379 | [[package]] 380 | name = "gdk-sys" 381 | version = "0.15.1" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" 384 | dependencies = [ 385 | "cairo-sys-rs", 386 | "gdk-pixbuf-sys", 387 | "gio-sys", 388 | "glib-sys", 389 | "gobject-sys", 390 | "libc", 391 | "pango-sys", 392 | "pkg-config", 393 | "system-deps", 394 | ] 395 | 396 | [[package]] 397 | name = "getrandom" 398 | version = "0.2.6" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" 401 | dependencies = [ 402 | "cfg-if", 403 | "libc", 404 | "wasi 0.10.2+wasi-snapshot-preview1", 405 | ] 406 | 407 | [[package]] 408 | name = "gio" 409 | version = "0.15.10" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "76cd21a7a674ea811749661012512b0ba5237ba404ccbcab2850db5537549b64" 412 | dependencies = [ 413 | "bitflags", 414 | "futures-channel", 415 | "futures-core", 416 | "futures-io", 417 | "gio-sys", 418 | "glib", 419 | "libc", 420 | "once_cell", 421 | "thiserror", 422 | ] 423 | 424 | [[package]] 425 | name = "gio-sys" 426 | version = "0.15.10" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" 429 | dependencies = [ 430 | "glib-sys", 431 | "gobject-sys", 432 | "libc", 433 | "system-deps", 434 | "winapi", 435 | ] 436 | 437 | [[package]] 438 | name = "glib" 439 | version = "0.15.10" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "a826fad715b57834920839d7a594c3b5e416358c7d790bdaba847a40d7c1d96d" 442 | dependencies = [ 443 | "bitflags", 444 | "futures-channel", 445 | "futures-core", 446 | "futures-executor", 447 | "futures-task", 448 | "glib-macros", 449 | "glib-sys", 450 | "gobject-sys", 451 | "libc", 452 | "once_cell", 453 | "smallvec", 454 | "thiserror", 455 | ] 456 | 457 | [[package]] 458 | name = "glib-macros" 459 | version = "0.15.10" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "dac4d47c544af67747652ab1865ace0ffa1155709723ac4f32e97587dd4735b2" 462 | dependencies = [ 463 | "anyhow", 464 | "heck", 465 | "proc-macro-crate", 466 | "proc-macro-error", 467 | "proc-macro2", 468 | "quote", 469 | "syn", 470 | ] 471 | 472 | [[package]] 473 | name = "glib-sys" 474 | version = "0.15.10" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" 477 | dependencies = [ 478 | "libc", 479 | "system-deps", 480 | ] 481 | 482 | [[package]] 483 | name = "gobject-sys" 484 | version = "0.15.10" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" 487 | dependencies = [ 488 | "glib-sys", 489 | "libc", 490 | "system-deps", 491 | ] 492 | 493 | [[package]] 494 | name = "gtk" 495 | version = "0.15.4" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "5f2d1326b36af927fe46ae2f89a8fec38c6f0d279ebc5ef07ffeeabb70300bfc" 498 | dependencies = [ 499 | "atk", 500 | "bitflags", 501 | "cairo-rs", 502 | "field-offset", 503 | "futures-channel", 504 | "gdk", 505 | "gdk-pixbuf", 506 | "gio", 507 | "glib", 508 | "gtk-sys", 509 | "gtk3-macros", 510 | "libc", 511 | "once_cell", 512 | "pango", 513 | "pkg-config", 514 | ] 515 | 516 | [[package]] 517 | name = "gtk-sys" 518 | version = "0.15.3" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" 521 | dependencies = [ 522 | "atk-sys", 523 | "cairo-sys-rs", 524 | "gdk-pixbuf-sys", 525 | "gdk-sys", 526 | "gio-sys", 527 | "glib-sys", 528 | "gobject-sys", 529 | "libc", 530 | "pango-sys", 531 | "system-deps", 532 | ] 533 | 534 | [[package]] 535 | name = "gtk-tray" 536 | version = "0.1.0" 537 | dependencies = [ 538 | "gtk", 539 | "stray", 540 | "tokio", 541 | ] 542 | 543 | [[package]] 544 | name = "gtk3-macros" 545 | version = "0.15.4" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" 548 | dependencies = [ 549 | "anyhow", 550 | "proc-macro-crate", 551 | "proc-macro-error", 552 | "proc-macro2", 553 | "quote", 554 | "syn", 555 | ] 556 | 557 | [[package]] 558 | name = "heck" 559 | version = "0.4.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 562 | 563 | [[package]] 564 | name = "hermit-abi" 565 | version = "0.1.19" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 568 | dependencies = [ 569 | "libc", 570 | ] 571 | 572 | [[package]] 573 | name = "hex" 574 | version = "0.4.3" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 577 | 578 | [[package]] 579 | name = "instant" 580 | version = "0.1.12" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 583 | dependencies = [ 584 | "cfg-if", 585 | ] 586 | 587 | [[package]] 588 | name = "lazy_static" 589 | version = "1.4.0" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 592 | 593 | [[package]] 594 | name = "libc" 595 | version = "0.2.124" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" 598 | 599 | [[package]] 600 | name = "lock_api" 601 | version = "0.4.7" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 604 | dependencies = [ 605 | "autocfg", 606 | "scopeguard", 607 | ] 608 | 609 | [[package]] 610 | name = "log" 611 | version = "0.4.16" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" 614 | dependencies = [ 615 | "cfg-if", 616 | ] 617 | 618 | [[package]] 619 | name = "memchr" 620 | version = "2.4.1" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 623 | 624 | [[package]] 625 | name = "memoffset" 626 | version = "0.6.5" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 629 | dependencies = [ 630 | "autocfg", 631 | ] 632 | 633 | [[package]] 634 | name = "mio" 635 | version = "0.8.2" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" 638 | dependencies = [ 639 | "libc", 640 | "log", 641 | "miow", 642 | "ntapi", 643 | "wasi 0.11.0+wasi-snapshot-preview1", 644 | "winapi", 645 | ] 646 | 647 | [[package]] 648 | name = "miow" 649 | version = "0.3.7" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 652 | dependencies = [ 653 | "winapi", 654 | ] 655 | 656 | [[package]] 657 | name = "nix" 658 | version = "0.23.1" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" 661 | dependencies = [ 662 | "bitflags", 663 | "cc", 664 | "cfg-if", 665 | "libc", 666 | "memoffset", 667 | ] 668 | 669 | [[package]] 670 | name = "ntapi" 671 | version = "0.3.7" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" 674 | dependencies = [ 675 | "winapi", 676 | ] 677 | 678 | [[package]] 679 | name = "num_cpus" 680 | version = "1.13.1" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 683 | dependencies = [ 684 | "hermit-abi", 685 | "libc", 686 | ] 687 | 688 | [[package]] 689 | name = "once_cell" 690 | version = "1.10.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 693 | 694 | [[package]] 695 | name = "ordered-stream" 696 | version = "0.0.1" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" 699 | dependencies = [ 700 | "futures-core", 701 | "pin-project-lite", 702 | ] 703 | 704 | [[package]] 705 | name = "pango" 706 | version = "0.15.10" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" 709 | dependencies = [ 710 | "bitflags", 711 | "glib", 712 | "libc", 713 | "once_cell", 714 | "pango-sys", 715 | ] 716 | 717 | [[package]] 718 | name = "pango-sys" 719 | version = "0.15.10" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" 722 | dependencies = [ 723 | "glib-sys", 724 | "gobject-sys", 725 | "libc", 726 | "system-deps", 727 | ] 728 | 729 | [[package]] 730 | name = "parking" 731 | version = "2.0.0" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 734 | 735 | [[package]] 736 | name = "parking_lot" 737 | version = "0.12.0" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 740 | dependencies = [ 741 | "lock_api", 742 | "parking_lot_core", 743 | ] 744 | 745 | [[package]] 746 | name = "parking_lot_core" 747 | version = "0.9.2" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" 750 | dependencies = [ 751 | "cfg-if", 752 | "libc", 753 | "redox_syscall", 754 | "smallvec", 755 | "windows-sys", 756 | ] 757 | 758 | [[package]] 759 | name = "pest" 760 | version = "2.1.3" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 763 | dependencies = [ 764 | "ucd-trie", 765 | ] 766 | 767 | [[package]] 768 | name = "pin-project-lite" 769 | version = "0.2.8" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" 772 | 773 | [[package]] 774 | name = "pin-utils" 775 | version = "0.1.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 778 | 779 | [[package]] 780 | name = "pkg-config" 781 | version = "0.3.25" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 784 | 785 | [[package]] 786 | name = "ppv-lite86" 787 | version = "0.2.16" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 790 | 791 | [[package]] 792 | name = "proc-macro-crate" 793 | version = "1.1.3" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" 796 | dependencies = [ 797 | "thiserror", 798 | "toml", 799 | ] 800 | 801 | [[package]] 802 | name = "proc-macro-error" 803 | version = "1.0.4" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 806 | dependencies = [ 807 | "proc-macro-error-attr", 808 | "proc-macro2", 809 | "quote", 810 | "syn", 811 | "version_check", 812 | ] 813 | 814 | [[package]] 815 | name = "proc-macro-error-attr" 816 | version = "1.0.4" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 819 | dependencies = [ 820 | "proc-macro2", 821 | "quote", 822 | "version_check", 823 | ] 824 | 825 | [[package]] 826 | name = "proc-macro2" 827 | version = "1.0.37" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" 830 | dependencies = [ 831 | "unicode-xid", 832 | ] 833 | 834 | [[package]] 835 | name = "quote" 836 | version = "1.0.18" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" 839 | dependencies = [ 840 | "proc-macro2", 841 | ] 842 | 843 | [[package]] 844 | name = "rand" 845 | version = "0.8.5" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 848 | dependencies = [ 849 | "libc", 850 | "rand_chacha", 851 | "rand_core", 852 | ] 853 | 854 | [[package]] 855 | name = "rand_chacha" 856 | version = "0.3.1" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 859 | dependencies = [ 860 | "ppv-lite86", 861 | "rand_core", 862 | ] 863 | 864 | [[package]] 865 | name = "rand_core" 866 | version = "0.6.3" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 869 | dependencies = [ 870 | "getrandom", 871 | ] 872 | 873 | [[package]] 874 | name = "redox_syscall" 875 | version = "0.2.13" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 878 | dependencies = [ 879 | "bitflags", 880 | ] 881 | 882 | [[package]] 883 | name = "regex" 884 | version = "1.5.5" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 887 | dependencies = [ 888 | "aho-corasick", 889 | "memchr", 890 | "regex-syntax", 891 | ] 892 | 893 | [[package]] 894 | name = "regex-syntax" 895 | version = "0.6.25" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 898 | 899 | [[package]] 900 | name = "rustc_version" 901 | version = "0.3.3" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" 904 | dependencies = [ 905 | "semver", 906 | ] 907 | 908 | [[package]] 909 | name = "scopeguard" 910 | version = "1.1.0" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 913 | 914 | [[package]] 915 | name = "semver" 916 | version = "0.11.0" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 919 | dependencies = [ 920 | "semver-parser", 921 | ] 922 | 923 | [[package]] 924 | name = "semver-parser" 925 | version = "0.10.2" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 928 | dependencies = [ 929 | "pest", 930 | ] 931 | 932 | [[package]] 933 | name = "serde" 934 | version = "1.0.136" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 937 | dependencies = [ 938 | "serde_derive", 939 | ] 940 | 941 | [[package]] 942 | name = "serde_derive" 943 | version = "1.0.136" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 946 | dependencies = [ 947 | "proc-macro2", 948 | "quote", 949 | "syn", 950 | ] 951 | 952 | [[package]] 953 | name = "serde_repr" 954 | version = "0.1.7" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" 957 | dependencies = [ 958 | "proc-macro2", 959 | "quote", 960 | "syn", 961 | ] 962 | 963 | [[package]] 964 | name = "sha1" 965 | version = "0.6.1" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" 968 | dependencies = [ 969 | "sha1_smol", 970 | ] 971 | 972 | [[package]] 973 | name = "sha1_smol" 974 | version = "1.0.0" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" 977 | 978 | [[package]] 979 | name = "signal-hook-registry" 980 | version = "1.4.0" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 983 | dependencies = [ 984 | "libc", 985 | ] 986 | 987 | [[package]] 988 | name = "slab" 989 | version = "0.4.6" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" 992 | 993 | [[package]] 994 | name = "smallvec" 995 | version = "1.8.0" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 998 | 999 | [[package]] 1000 | name = "socket2" 1001 | version = "0.4.4" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 1004 | dependencies = [ 1005 | "libc", 1006 | "winapi", 1007 | ] 1008 | 1009 | [[package]] 1010 | name = "static_assertions" 1011 | version = "1.1.0" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1014 | 1015 | [[package]] 1016 | name = "stray" 1017 | version = "0.1.0" 1018 | dependencies = [ 1019 | "anyhow", 1020 | "byteorder", 1021 | "event-listener", 1022 | "serde", 1023 | "tokio", 1024 | "tokio-stream", 1025 | "zbus", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "syn" 1030 | version = "1.0.91" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" 1033 | dependencies = [ 1034 | "proc-macro2", 1035 | "quote", 1036 | "unicode-xid", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "system-deps" 1041 | version = "6.0.2" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709" 1044 | dependencies = [ 1045 | "cfg-expr", 1046 | "heck", 1047 | "pkg-config", 1048 | "toml", 1049 | "version-compare", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "thiserror" 1054 | version = "1.0.30" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1057 | dependencies = [ 1058 | "thiserror-impl", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "thiserror-impl" 1063 | version = "1.0.30" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1066 | dependencies = [ 1067 | "proc-macro2", 1068 | "quote", 1069 | "syn", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "tokio" 1074 | version = "1.17.0" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" 1077 | dependencies = [ 1078 | "bytes", 1079 | "libc", 1080 | "memchr", 1081 | "mio", 1082 | "num_cpus", 1083 | "once_cell", 1084 | "parking_lot", 1085 | "pin-project-lite", 1086 | "signal-hook-registry", 1087 | "socket2", 1088 | "tokio-macros", 1089 | "winapi", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "tokio-macros" 1094 | version = "1.7.0" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 1097 | dependencies = [ 1098 | "proc-macro2", 1099 | "quote", 1100 | "syn", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "tokio-stream" 1105 | version = "0.1.8" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" 1108 | dependencies = [ 1109 | "futures-core", 1110 | "pin-project-lite", 1111 | "tokio", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "toml" 1116 | version = "0.5.9" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1119 | dependencies = [ 1120 | "serde", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "ucd-trie" 1125 | version = "0.1.3" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1128 | 1129 | [[package]] 1130 | name = "unicode-xid" 1131 | version = "0.2.2" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1134 | 1135 | [[package]] 1136 | name = "version-compare" 1137 | version = "0.1.0" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" 1140 | 1141 | [[package]] 1142 | name = "version_check" 1143 | version = "0.9.4" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1146 | 1147 | [[package]] 1148 | name = "waker-fn" 1149 | version = "1.1.0" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 1152 | 1153 | [[package]] 1154 | name = "wasi" 1155 | version = "0.10.2+wasi-snapshot-preview1" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1158 | 1159 | [[package]] 1160 | name = "wasi" 1161 | version = "0.11.0+wasi-snapshot-preview1" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1164 | 1165 | [[package]] 1166 | name = "winapi" 1167 | version = "0.3.9" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1170 | dependencies = [ 1171 | "winapi-i686-pc-windows-gnu", 1172 | "winapi-x86_64-pc-windows-gnu", 1173 | ] 1174 | 1175 | [[package]] 1176 | name = "winapi-i686-pc-windows-gnu" 1177 | version = "0.4.0" 1178 | source = "registry+https://github.com/rust-lang/crates.io-index" 1179 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1180 | 1181 | [[package]] 1182 | name = "winapi-x86_64-pc-windows-gnu" 1183 | version = "0.4.0" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1186 | 1187 | [[package]] 1188 | name = "windows-sys" 1189 | version = "0.34.0" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" 1192 | dependencies = [ 1193 | "windows_aarch64_msvc", 1194 | "windows_i686_gnu", 1195 | "windows_i686_msvc", 1196 | "windows_x86_64_gnu", 1197 | "windows_x86_64_msvc", 1198 | ] 1199 | 1200 | [[package]] 1201 | name = "windows_aarch64_msvc" 1202 | version = "0.34.0" 1203 | source = "registry+https://github.com/rust-lang/crates.io-index" 1204 | checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" 1205 | 1206 | [[package]] 1207 | name = "windows_i686_gnu" 1208 | version = "0.34.0" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" 1211 | 1212 | [[package]] 1213 | name = "windows_i686_msvc" 1214 | version = "0.34.0" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" 1217 | 1218 | [[package]] 1219 | name = "windows_x86_64_gnu" 1220 | version = "0.34.0" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" 1223 | 1224 | [[package]] 1225 | name = "windows_x86_64_msvc" 1226 | version = "0.34.0" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" 1229 | 1230 | [[package]] 1231 | name = "zbus" 1232 | version = "2.1.1" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "7bb86f3d4592e26a48b2719742aec94f8ae6238ebde20d98183ee185d1275e9a" 1235 | dependencies = [ 1236 | "async-broadcast", 1237 | "async-channel", 1238 | "async-executor", 1239 | "async-lock", 1240 | "async-recursion", 1241 | "async-task", 1242 | "async-trait", 1243 | "byteorder", 1244 | "derivative", 1245 | "enumflags2", 1246 | "event-listener", 1247 | "futures-core", 1248 | "futures-sink", 1249 | "futures-util", 1250 | "hex", 1251 | "lazy_static", 1252 | "nix", 1253 | "once_cell", 1254 | "ordered-stream", 1255 | "rand", 1256 | "serde", 1257 | "serde_repr", 1258 | "sha1", 1259 | "static_assertions", 1260 | "tokio", 1261 | "winapi", 1262 | "zbus_macros", 1263 | "zbus_names", 1264 | "zvariant", 1265 | ] 1266 | 1267 | [[package]] 1268 | name = "zbus_macros" 1269 | version = "2.1.1" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "36823cc10fddc3c6b19f048903262dacaf8274170e9a255784bdd8b4570a8040" 1272 | dependencies = [ 1273 | "proc-macro-crate", 1274 | "proc-macro2", 1275 | "quote", 1276 | "regex", 1277 | "syn", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "zbus_names" 1282 | version = "2.1.0" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "45dfcdcf87b71dad505d30cc27b1b7b88a64b6d1c435648f48f9dbc1fdc4b7e1" 1285 | dependencies = [ 1286 | "serde", 1287 | "static_assertions", 1288 | "zvariant", 1289 | ] 1290 | 1291 | [[package]] 1292 | name = "zvariant" 1293 | version = "3.1.2" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "49ea5dc38b2058fae6a5b79009388143dadce1e91c26a67f984a0fc0381c8033" 1296 | dependencies = [ 1297 | "byteorder", 1298 | "enumflags2", 1299 | "libc", 1300 | "serde", 1301 | "static_assertions", 1302 | "zvariant_derive", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "zvariant_derive" 1307 | version = "3.1.2" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "8c2cecc5a61c2a053f7f653a24cd15b3b0195d7f7ddb5042c837fb32e161fb7a" 1310 | dependencies = [ 1311 | "proc-macro-crate", 1312 | "proc-macro2", 1313 | "quote", 1314 | "syn", 1315 | ] 1316 | -------------------------------------------------------------------------------- /gtk-tray/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gtk-tray" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | gtk = "0.15.4" 10 | stray = { path = "../stray" } 11 | tokio = { version = "1.17.0", features = ["full"] } 12 | once_cell = "1.10.0" 13 | tracing-subscriber = "0.3.16" -------------------------------------------------------------------------------- /gtk-tray/src/main.rs: -------------------------------------------------------------------------------- 1 | use gtk::glib; 2 | use gtk::prelude::*; 3 | use gtk::{IconLookupFlags, IconTheme, Image, Menu, MenuBar, MenuItem, SeparatorMenuItem}; 4 | use once_cell::sync::Lazy; 5 | use std::collections::HashMap; 6 | use std::sync::Mutex; 7 | use std::thread; 8 | use stray::message::menu::{MenuType, TrayMenu}; 9 | use stray::message::tray::{IconPixmap, StatusNotifierItem}; 10 | use stray::message::{NotifierItemCommand, NotifierItemMessage}; 11 | use stray::StatusNotifierWatcher; 12 | use tokio::runtime::Runtime; 13 | use tokio::sync::mpsc; 14 | 15 | struct NotifierItem { 16 | item: StatusNotifierItem, 17 | menu: Option, 18 | } 19 | 20 | pub struct StatusNotifierWrapper { 21 | menu: stray::message::menu::MenuItem, 22 | } 23 | 24 | static STATE: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); 25 | 26 | impl StatusNotifierWrapper { 27 | fn into_menu_item( 28 | self, 29 | sender: mpsc::Sender, 30 | notifier_address: String, 31 | menu_path: String, 32 | ) -> MenuItem { 33 | let item: Box> = match self.menu.menu_type { 34 | MenuType::Separator => Box::new(SeparatorMenuItem::new()), 35 | MenuType::Standard => Box::new(MenuItem::with_label(self.menu.label.as_str())), 36 | }; 37 | 38 | let item = (*item).as_ref().clone(); 39 | 40 | { 41 | let sender = sender.clone(); 42 | let notifier_address = notifier_address.clone(); 43 | let menu_path = menu_path.clone(); 44 | 45 | item.connect_activate(move |_item| { 46 | sender 47 | .try_send(NotifierItemCommand::MenuItemClicked { 48 | submenu_id: self.menu.id, 49 | menu_path: menu_path.clone(), 50 | notifier_address: notifier_address.clone(), 51 | }) 52 | .unwrap(); 53 | }); 54 | }; 55 | 56 | let submenu = Menu::new(); 57 | if !self.menu.submenu.is_empty() { 58 | for submenu_item in self.menu.submenu.iter().cloned() { 59 | let submenu_item = StatusNotifierWrapper { menu: submenu_item }; 60 | let submenu_item = submenu_item.into_menu_item( 61 | sender.clone(), 62 | notifier_address.clone(), 63 | menu_path.clone(), 64 | ); 65 | submenu.append(&submenu_item); 66 | } 67 | 68 | item.set_submenu(Some(&submenu)); 69 | } 70 | 71 | item 72 | } 73 | } 74 | 75 | impl NotifierItem { 76 | fn get_icon(&self) -> Option { 77 | match &self.item.icon_pixmap { 78 | None => self.get_icon_from_theme(), 79 | Some(pixmaps) => self.get_icon_from_pixmaps(pixmaps), 80 | } 81 | } 82 | 83 | fn get_icon_from_pixmaps(&self, pixmaps: &[IconPixmap]) -> Option { 84 | let pixmap = pixmaps 85 | .iter() 86 | .find(|pm| pm.height > 20 && pm.height < 32) 87 | .expect("No icon of suitable size found"); 88 | 89 | let pixbuf = gtk::gdk_pixbuf::Pixbuf::new( 90 | gtk::gdk_pixbuf::Colorspace::Rgb, 91 | true, 92 | 8, 93 | pixmap.width, 94 | pixmap.height, 95 | ) 96 | .expect("Failed to allocate pixbuf"); 97 | 98 | for y in 0..pixmap.height { 99 | for x in 0..pixmap.width { 100 | let index = (y * pixmap.width + x) * 4; 101 | let a = pixmap.pixels[index as usize]; 102 | let r = pixmap.pixels[(index + 1) as usize]; 103 | let g = pixmap.pixels[(index + 2) as usize]; 104 | let b = pixmap.pixels[(index + 3) as usize]; 105 | pixbuf.put_pixel(x as u32, y as u32, r, g, b, a); 106 | } 107 | } 108 | 109 | Some(Image::from_pixbuf(Some(&pixbuf))) 110 | } 111 | 112 | fn get_icon_from_theme(&self) -> Option { 113 | let theme = gtk::IconTheme::default().unwrap_or(IconTheme::new()); 114 | theme.rescan_if_needed(); 115 | 116 | if let Some(path) = self.item.icon_theme_path.as_ref() { 117 | theme.append_search_path(path); 118 | } 119 | 120 | let icon_name = self.item.icon_name.as_ref().unwrap(); 121 | let icon = theme.lookup_icon(icon_name, 24, IconLookupFlags::GENERIC_FALLBACK); 122 | 123 | icon.map(|i| Image::from_pixbuf(i.load_icon().ok().as_ref())) 124 | } 125 | } 126 | 127 | fn main() { 128 | tracing_subscriber::fmt::init(); 129 | 130 | let application = gtk::Application::new( 131 | Some("com.github.gtk-rs.examples.menu_bar_system"), 132 | Default::default(), 133 | ); 134 | 135 | application.connect_activate(build_ui); 136 | 137 | application.run(); 138 | } 139 | 140 | fn build_ui(application: >k::Application) { 141 | let window = gtk::ApplicationWindow::new(application); 142 | window.set_title("System menu bar"); 143 | window.set_border_width(10); 144 | window.set_position(gtk::WindowPosition::Center); 145 | window.set_default_size(350, 70); 146 | 147 | let menu_bar = MenuBar::new(); 148 | window.add(&menu_bar); 149 | let (sender, receiver) = mpsc::channel(32); 150 | let (cmd_tx, cmd_rx) = mpsc::channel(32); 151 | 152 | spawn_local_handler(menu_bar, receiver, cmd_tx); 153 | start_communication_thread(sender, cmd_rx); 154 | window.show_all(); 155 | } 156 | 157 | fn spawn_local_handler( 158 | v_box: MenuBar, 159 | mut receiver: mpsc::Receiver, 160 | cmd_tx: mpsc::Sender, 161 | ) { 162 | let main_context = glib::MainContext::default(); 163 | let future = async move { 164 | while let Some(item) = receiver.recv().await { 165 | let mut state = STATE.lock().unwrap(); 166 | 167 | match item { 168 | NotifierItemMessage::Update { 169 | address: id, 170 | item, 171 | menu, 172 | } => { 173 | state.insert(id, NotifierItem { item: *item, menu }); 174 | } 175 | NotifierItemMessage::Remove { address } => { 176 | state.remove(&address); 177 | } 178 | } 179 | 180 | for child in v_box.children() { 181 | v_box.remove(&child); 182 | } 183 | 184 | for (address, notifier_item) in state.iter() { 185 | if let Some(icon) = notifier_item.get_icon() { 186 | // Create the menu 187 | 188 | let menu_item = MenuItem::new(); 189 | let menu_item_box = gtk::Box::default(); 190 | menu_item_box.add(&icon); 191 | menu_item.add(&menu_item_box); 192 | 193 | if let Some(tray_menu) = ¬ifier_item.menu { 194 | let menu = Menu::new(); 195 | tray_menu 196 | .submenus 197 | .iter() 198 | .map(|submenu| StatusNotifierWrapper { 199 | menu: submenu.to_owned(), 200 | }) 201 | .map(|item| { 202 | let menu_path = 203 | notifier_item.item.menu.as_ref().unwrap().to_string(); 204 | let address = address.to_string(); 205 | item.into_menu_item(cmd_tx.clone(), address, menu_path) 206 | }) 207 | .for_each(|item| menu.append(&item)); 208 | 209 | if !tray_menu.submenus.is_empty() { 210 | menu_item.set_submenu(Some(&menu)); 211 | } 212 | } 213 | v_box.append(&menu_item); 214 | }; 215 | 216 | v_box.show_all(); 217 | } 218 | } 219 | }; 220 | 221 | main_context.spawn_local(future); 222 | } 223 | 224 | fn start_communication_thread( 225 | sender: mpsc::Sender, 226 | cmd_rx: mpsc::Receiver, 227 | ) { 228 | thread::spawn(move || { 229 | let runtime = Runtime::new().expect("Failed to create tokio RT"); 230 | 231 | runtime.block_on(async { 232 | let tray = StatusNotifierWatcher::new(cmd_rx).await.unwrap(); 233 | let mut host = tray.create_notifier_host("MyHost").await.unwrap(); 234 | 235 | while let Ok(message) = host.recv().await { 236 | sender 237 | .send(message) 238 | .await 239 | .expect("failed to send message to UI"); 240 | } 241 | 242 | host.destroy().await.unwrap(); 243 | }) 244 | }); 245 | } 246 | -------------------------------------------------------------------------------- /stray/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stray" 3 | version = "0.1.3" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "A freedesktop StatusNotifierWatcher implementation" 7 | repository = "https://github.com/oknozor/stray" 8 | readme = "README.md" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | tokio = { version = "1.17.0", features = ["rt-multi-thread", "macros"] } 14 | tokio-stream = "0.1.8" 15 | zbus = { version = "3.13.1", default-features = false, features = ["tokio", "gvariant"] } 16 | anyhow = "1.0.56" 17 | serde = "1.0.136" 18 | byteorder = "1.4.3" 19 | chrono = "0.4.19" 20 | log = "0.4.17" 21 | thiserror = "1.0.31" 22 | tracing = "0.1" 23 | 24 | [[example]] 25 | path = "examples/simple.rs" 26 | name = "simple" 27 | -------------------------------------------------------------------------------- /stray/README.md: -------------------------------------------------------------------------------- 1 | # Stray 2 | 3 | Stray is a minimal [SystemNotifierWatcher](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/) 4 | implementation which goal is to provide a minimalistic API to access tray icons and menu. 5 | 6 | ## Examples 7 | 8 | ### Start the system tray and listen for changes 9 | ```rust, ignore 10 | use stray::{SystemTray}; 11 | use tokio_stream::StreamExt; 12 | use stray::message::NotifierItemMessage; 13 | use stray::message::NotifierItemCommand; 14 | 15 | #[tokio::main] 16 | async fn main() { 17 | 18 | // A mpsc channel to send menu activation requests later 19 | let (ui_tx, ui_rx) = tokio::sync::mpsc::channel(32); 20 | let mut tray = SystemTray::new(ui_rx).await; 21 | 22 | while let Some(message) = tray.next().await { 23 | match message { 24 | NotifierItemMessage::Update { address: id, item, menu } => { 25 | println!("NotifierItem updated : 26 | id = {id}, 27 | item = {item:?}, 28 | menu = {menu:?}" 29 | ) 30 | } 31 | NotifierItemMessage::Remove { address: id } => { 32 | println!("NotifierItem removed : id = {id}"); 33 | } 34 | } 35 | } 36 | } 37 | ``` 38 | 39 | ### Send menu activation request to the system tray 40 | 41 | ```rust, ignore 42 | // Assuming we stored our menu items in some UI state we can send menu item activation request: 43 | use stray::message::NotifierItemCommand; 44 | 45 | ui_tx.clone().try_send(NotifierItemCommand::MenuItemClicked { 46 | // The submenu to activate 47 | submenu_id: 32, 48 | // dbus menu path, available in the `StatusNotifierItem` 49 | menu_path: "/org/ayatana/NotificationItem/Element1/Menu".to_string(), 50 | // the notifier address we previously got from `NotifierItemMessage::Update` 51 | notifier_address: ":1.2161".to_string(), 52 | }).unwrap(); 53 | ``` 54 | 55 | ### Gtk example 56 | 57 | For a detailed, real life example, you can take a look at the [gtk-tray](https://github.com/oknozor/stray/tree/main/gtk-tray). 58 | 59 | ```shell 60 | git clone git@github.com:oknozor/stray.git 61 | cd stray/gtk-tray 62 | cargo run 63 | ``` 64 | -------------------------------------------------------------------------------- /stray/examples/simple.rs: -------------------------------------------------------------------------------- 1 | use stray::StatusNotifierWatcher; 2 | use tokio::join; 3 | use tokio::sync::mpsc; 4 | 5 | #[tokio::main] 6 | async fn main() -> stray::error::Result<()> { 7 | let (_cmd_tx, cmd_rx) = mpsc::channel(10); 8 | let tray = StatusNotifierWatcher::new(cmd_rx).await?; 9 | 10 | let mut host_one = tray.create_notifier_host("host_one").await.unwrap(); 11 | let mut host_two = tray.create_notifier_host("host_two").await.unwrap(); 12 | 13 | let one = tokio::spawn(async move { 14 | while let Ok(mesage) = host_one.recv().await { 15 | println!("Message from host one {:?}", mesage); 16 | } 17 | }); 18 | 19 | let two = tokio::spawn(async move { 20 | let mut count = 0; 21 | while let Ok(mesage) = host_two.recv().await { 22 | count += 1; 23 | if count > 5 { 24 | break; 25 | } 26 | println!("Message from host two {:?}", mesage); 27 | } 28 | 29 | host_two.destroy().await?; 30 | stray::error::Result::<()>::Ok(()) 31 | }); 32 | 33 | let _ = join!(one, two); 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /stray/src/dbus/dbusmenu_proxy.rs: -------------------------------------------------------------------------------- 1 | //! # DBus interface proxy for: `com.canonical.dbusmenu` 2 | //! 3 | //! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data. 4 | //! Source: `org.cannonical.indicator.xml`. 5 | //! 6 | //! You may prefer to adapt it, instead of using it verbatim. 7 | //! 8 | //! More information can be found in the 9 | //! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) 10 | //! section of the zbus documentation. 11 | //! 12 | 13 | use std::collections::HashMap; 14 | 15 | use zbus::dbus_proxy; 16 | use zbus::zvariant::OwnedValue; 17 | 18 | use serde::{Deserialize, Serialize}; 19 | use zbus::zvariant::Type; 20 | 21 | #[derive(Deserialize, Serialize, Type, PartialEq, Debug)] 22 | pub struct MenuLayout { 23 | pub id: u32, 24 | pub fields: SubMenuLayout, 25 | } 26 | 27 | #[derive(Deserialize, Serialize, Type, PartialEq, Debug)] 28 | pub struct SubMenuLayout { 29 | pub id: i32, 30 | pub fields: HashMap, 31 | pub submenus: Vec, 32 | } 33 | 34 | #[allow(dead_code)] 35 | type GroupProperties = Vec<(i32, HashMap)>; 36 | 37 | #[dbus_proxy(interface = "com.canonical.dbusmenu", assume_defaults = true)] 38 | trait DBusMenu { 39 | fn about_to_show(&self, id: i32) -> zbus::Result; 40 | 41 | fn event( 42 | &self, 43 | id: i32, 44 | event_id: &str, 45 | data: &zbus::zvariant::Value<'_>, 46 | timestamp: u32, 47 | ) -> zbus::Result<()>; 48 | 49 | fn get_group_properties( 50 | &self, 51 | ids: &[i32], 52 | property_names: &[&str], 53 | ) -> zbus::Result<(u32, GroupProperties)>; 54 | 55 | fn get_layout( 56 | &self, 57 | parent_id: i32, 58 | recursion_depth: i32, 59 | property_names: &[&str], 60 | ) -> zbus::Result; 61 | 62 | fn get_property(&self, id: i32, name: &str) -> zbus::Result; 63 | 64 | #[dbus_proxy(signal)] 65 | fn item_activation_requested(&self, id: i32, timestamp: u32) -> zbus::Result<()>; 66 | 67 | #[dbus_proxy(signal)] 68 | fn items_properties_updated( 69 | &self, 70 | updated_props: Vec<(i32, HashMap<&str, zbus::zvariant::Value<'_>>)>, 71 | removed_props: Vec<(i32, Vec<&str>)>, 72 | ) -> zbus::Result<()>; 73 | 74 | #[dbus_proxy(signal)] 75 | fn layout_updated(&self, revision: u32, parent: i32) -> zbus::Result<()>; 76 | 77 | #[dbus_proxy(property)] 78 | fn status(&self) -> zbus::Result; 79 | 80 | #[dbus_proxy(property)] 81 | fn version(&self) -> zbus::Result; 82 | } 83 | -------------------------------------------------------------------------------- /stray/src/dbus/mod.rs: -------------------------------------------------------------------------------- 1 | pub(super) mod dbusmenu_proxy; 2 | pub(super) mod notifier_item_proxy; 3 | pub(super) mod notifier_watcher_proxy; 4 | pub(super) mod notifier_watcher_service; 5 | -------------------------------------------------------------------------------- /stray/src/dbus/notifier_item_proxy.rs: -------------------------------------------------------------------------------- 1 | //! # DBus interface proxy for: `org.kde.StatusNotifierItem` 2 | //! 3 | //! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data. 4 | //! Source: `status-notifier-item.xml`. 5 | //! 6 | //! You may prefer to adapt it, instead of using it verbatim. 7 | //! 8 | //! More information can be found in the 9 | //! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) 10 | //! section of the zbus documentation. 11 | //! 12 | 13 | use zbus::dbus_proxy; 14 | 15 | #[allow(dead_code)] 16 | type ToolTip = (String, Vec<(i32, i32, Vec)>); 17 | 18 | #[dbus_proxy(interface = "org.kde.StatusNotifierItem", assume_defaults = true)] 19 | trait StatusNotifierItem { 20 | /// Activate method 21 | fn activate(&self, x: i32, y: i32) -> zbus::Result<()>; 22 | 23 | /// ContextMenu method 24 | fn context_menu(&self, x: i32, y: i32) -> zbus::Result<()>; 25 | 26 | /// Scroll method 27 | fn scroll(&self, delta: i32, orientation: &str) -> zbus::Result<()>; 28 | 29 | /// SecondaryActivate method 30 | fn secondary_activate(&self, x: i32, y: i32) -> zbus::Result<()>; 31 | 32 | /// NewAttentionIcon signal 33 | #[dbus_proxy(signal)] 34 | fn new_attention_icon(&self) -> zbus::Result<()>; 35 | 36 | /// NewIcon signal 37 | #[dbus_proxy(signal)] 38 | fn new_icon(&self) -> zbus::Result<()>; 39 | 40 | /// NewOverlayIcon signal 41 | #[dbus_proxy(signal)] 42 | fn new_overlay_icon(&self) -> zbus::Result<()>; 43 | 44 | /// NewStatus signal 45 | #[dbus_proxy(signal)] 46 | fn new_status(&self, status: &str) -> zbus::Result<()>; 47 | 48 | /// NewTitle signal 49 | #[dbus_proxy(signal)] 50 | fn new_title(&self) -> zbus::Result<()>; 51 | 52 | /// NewToolTip signal 53 | #[dbus_proxy(signal)] 54 | fn new_tool_tip(&self) -> zbus::Result<()>; 55 | 56 | /// AttentionIconName property 57 | #[dbus_proxy(property)] 58 | fn attention_icon_name(&self) -> zbus::Result; 59 | 60 | /// AttentionIconPixmap property 61 | #[dbus_proxy(property)] 62 | fn attention_icon_pixmap(&self) -> zbus::Result)>>; 63 | 64 | /// AttentionMovieName property 65 | #[dbus_proxy(property)] 66 | fn attention_movie_name(&self) -> zbus::Result; 67 | 68 | /// Category property 69 | #[dbus_proxy(property)] 70 | fn category(&self) -> zbus::Result; 71 | 72 | /// IconName property 73 | #[dbus_proxy(property)] 74 | fn icon_name(&self) -> zbus::Result; 75 | 76 | /// IconPixmap property 77 | #[dbus_proxy(property)] 78 | fn icon_pixmap(&self) -> zbus::Result)>>; 79 | 80 | /// IconThemePath property 81 | #[dbus_proxy(property)] 82 | fn icon_theme_path(&self) -> zbus::Result; 83 | 84 | /// Id property 85 | #[dbus_proxy(property)] 86 | fn id(&self) -> zbus::Result; 87 | 88 | /// ItemIsMenu property 89 | #[dbus_proxy(property)] 90 | fn item_is_menu(&self) -> zbus::Result; 91 | 92 | /// Menu property 93 | #[dbus_proxy(property)] 94 | fn menu(&self) -> zbus::Result; 95 | 96 | /// OverlayIconName property 97 | #[dbus_proxy(property)] 98 | fn overlay_icon_name(&self) -> zbus::Result; 99 | 100 | /// OverlayIconPixmap property 101 | #[dbus_proxy(property)] 102 | fn overlay_icon_pixmap(&self) -> zbus::Result)>>; 103 | 104 | /// Status property 105 | #[dbus_proxy(property)] 106 | fn status(&self) -> zbus::Result; 107 | 108 | /// Title property 109 | #[dbus_proxy(property)] 110 | fn title(&self) -> zbus::Result; 111 | 112 | /// ToolTip property 113 | #[dbus_proxy(property)] 114 | fn tool_tip(&self) -> zbus::Result; 115 | } 116 | -------------------------------------------------------------------------------- /stray/src/dbus/notifier_watcher_proxy.rs: -------------------------------------------------------------------------------- 1 | //! # DBus interface proxy for: `org.kde.StatusNotifierWatcher` 2 | //! 3 | //! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data. 4 | //! Source: `notfifier-watcher.xml`. 5 | //! 6 | //! You may prefer to adapt it, instead of using it verbatim. 7 | //! 8 | //! More information can be found in the 9 | //! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) 10 | //! section of the zbus documentation. 11 | //! 12 | 13 | use zbus::dbus_proxy; 14 | 15 | #[dbus_proxy( 16 | interface = "org.kde.StatusNotifierWatcher", 17 | default_path = "/StatusNotifierWatcher" 18 | )] 19 | pub(crate) trait StatusNotifierWatcher { 20 | fn register_status_notifier_host(&self, service: &str) -> zbus::Result<()>; 21 | 22 | fn unregister_status_notifier_item(&self, service: &str) -> zbus::Result<()>; 23 | 24 | fn register_status_notifier_item(&self, service: &str) -> zbus::Result<()>; 25 | 26 | #[dbus_proxy(signal)] 27 | fn status_notifier_host_registered(&self) -> zbus::Result<()>; 28 | 29 | #[dbus_proxy(signal)] 30 | fn status_notifier_host_unregistered(&self) -> zbus::Result<()>; 31 | 32 | #[dbus_proxy(signal)] 33 | fn status_notifier_item_registered(&self, service: &str) -> zbus::Result<()>; 34 | 35 | #[dbus_proxy(signal)] 36 | fn status_notifier_item_unregistered(&self, service: &str) -> zbus::Result<()>; 37 | 38 | #[dbus_proxy(property)] 39 | fn is_status_notifier_host_registered(&self) -> zbus::Result; 40 | 41 | #[dbus_proxy(property)] 42 | fn protocol_version(&self) -> zbus::Result; 43 | 44 | #[dbus_proxy(property)] 45 | fn registered_status_notifier_items(&self) -> zbus::Result>; 46 | } 47 | -------------------------------------------------------------------------------- /stray/src/dbus/notifier_watcher_service.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use tokio::sync::broadcast; 3 | 4 | use zbus::dbus_interface; 5 | use zbus::Result; 6 | use zbus::{MessageHeader, SignalContext}; 7 | 8 | use crate::NotifierItemMessage; 9 | 10 | pub struct DbusNotifierWatcher { 11 | pub status_notifier_hosts: HashSet, 12 | pub registered_status_notifier_items: HashSet, 13 | pub protocol_version: i32, 14 | pub is_status_notifier_host_registered: bool, 15 | pub sender: broadcast::Sender, 16 | } 17 | 18 | impl DbusNotifierWatcher { 19 | pub(crate) fn new(sender: broadcast::Sender) -> Self { 20 | DbusNotifierWatcher { 21 | registered_status_notifier_items: HashSet::new(), 22 | protocol_version: 0, 23 | is_status_notifier_host_registered: false, 24 | status_notifier_hosts: HashSet::new(), 25 | sender, 26 | } 27 | } 28 | } 29 | 30 | impl DbusNotifierWatcher { 31 | pub async fn remove_notifier(&mut self, notifier_address: &str) -> Result<()> { 32 | let to_remove = self 33 | .registered_status_notifier_items 34 | .iter() 35 | .find(|item| item.contains(notifier_address)) 36 | .cloned(); 37 | 38 | if let Some(notifier) = to_remove { 39 | let removed = self.registered_status_notifier_items.remove(¬ifier); 40 | if removed { 41 | self.sender 42 | .send(NotifierItemMessage::Remove { 43 | address: notifier_address.to_string(), 44 | }) 45 | .expect("Failed to dispatch notifier item removed message"); 46 | } 47 | } 48 | 49 | Ok(()) 50 | } 51 | } 52 | 53 | #[allow(dead_code)] 54 | #[dbus_interface(name = "org.kde.StatusNotifierWatcher")] 55 | impl DbusNotifierWatcher { 56 | async fn register_status_notifier_host( 57 | &mut self, 58 | service: &str, 59 | #[zbus(signal_context)] ctxt: SignalContext<'_>, 60 | ) { 61 | tracing::info!("StatusNotifierHost registered: '{}'", service); 62 | self.status_notifier_hosts.insert(service.to_string()); 63 | self.is_status_notifier_host_registered = true; 64 | self.is_status_notifier_host_registered_changed(&ctxt) 65 | .await 66 | .unwrap(); 67 | } 68 | 69 | async fn register_status_notifier_item( 70 | &mut self, 71 | service: &str, 72 | #[zbus(header)] header: MessageHeader<'_>, 73 | #[zbus(signal_context)] ctxt: SignalContext<'_>, 74 | ) { 75 | let address = header 76 | .sender() 77 | .expect("Failed to get message sender in header") 78 | .map(|name| name.to_string()) 79 | .expect("Failed to get unique name for notifier"); 80 | 81 | let notifier_item = format!("{}{}", address, service); 82 | 83 | self.registered_status_notifier_items 84 | .insert(notifier_item.clone()); 85 | 86 | tracing::info!("StatusNotifierItem registered: '{}'", notifier_item); 87 | 88 | Self::status_notifier_item_registered(&ctxt, ¬ifier_item) 89 | .await 90 | .unwrap(); 91 | } 92 | 93 | async fn unregister_status_notifier_item(&mut self, service: &str) { 94 | self.remove_notifier(service) 95 | .await 96 | .expect("Failed to unregister StatusNotifierItem") 97 | } 98 | 99 | #[dbus_interface(signal)] 100 | async fn status_notifier_host_registered(ctxt: &SignalContext<'_>) -> Result<()>; 101 | 102 | #[dbus_interface(signal)] 103 | async fn status_notifier_host_unregistered(ctxt: &SignalContext<'_>) -> Result<()>; 104 | 105 | #[dbus_interface(signal)] 106 | async fn status_notifier_item_registered(ctxt: &SignalContext<'_>, service: &str) 107 | -> Result<()>; 108 | 109 | #[dbus_interface(signal)] 110 | async fn status_notifier_item_unregistered( 111 | ctxt: &SignalContext<'_>, 112 | service: &str, 113 | ) -> Result<()>; 114 | 115 | #[dbus_interface(property)] 116 | async fn is_status_notifier_host_registered(&self) -> bool { 117 | self.is_status_notifier_host_registered 118 | } 119 | 120 | #[dbus_interface(property)] 121 | async fn protocol_version(&self) -> i32 { 122 | self.protocol_version 123 | } 124 | 125 | #[dbus_interface(property)] 126 | fn registered_status_notifier_items(&self) -> Vec { 127 | self.registered_status_notifier_items 128 | .iter() 129 | .cloned() 130 | .collect() 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /stray/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::NotifierItemMessage; 2 | use thiserror::Error; 3 | use tokio::sync::broadcast; 4 | 5 | pub type Result = std::result::Result; 6 | 7 | #[derive(Error, Debug)] 8 | pub enum StatusNotifierWatcherError { 9 | #[error("Dbus connection error")] 10 | DbusError(#[from] zbus::Error), 11 | #[error("Invalid DBus interface name")] 12 | InterfaceNameError(#[from] zbus::names::Error), 13 | #[error("Failed to call DBus standard interface method")] 14 | DBusStandardInterfaceError(#[from] zbus::fdo::Error), 15 | #[error("Serialization error")] 16 | ZvariantError(#[from] zbus::zvariant::Error), 17 | #[error("Service path {0} was not understood")] 18 | DbusAddressError(String), 19 | #[error("Failed to broadcast message to notifier hosts")] 20 | BroadCastSendError(#[from] broadcast::error::SendError), 21 | #[error("Error receiving broadcast message")] 22 | BroadCastRecvError(#[from] broadcast::error::RecvError), 23 | } 24 | -------------------------------------------------------------------------------- /stray/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str ! ("../README.md")] 2 | 3 | pub use tokio; 4 | use zbus::names::InterfaceName; 5 | 6 | use crate::dbus::dbusmenu_proxy::MenuLayout; 7 | use crate::message::tray::StatusNotifierItem; 8 | use dbus::notifier_watcher_service::DbusNotifierWatcher; 9 | 10 | mod dbus; 11 | mod notifier_host; 12 | mod notifier_watcher; 13 | 14 | pub mod error; 15 | /// Messages sent and received by the [`SystemTray`] 16 | pub mod message; 17 | 18 | pub use message::NotifierItemMessage; 19 | pub use notifier_watcher::StatusNotifierWatcher; 20 | -------------------------------------------------------------------------------- /stray/src/message/menu.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use std::str; 3 | use std::str::FromStr; 4 | 5 | use zbus::zvariant::{OwnedValue, Structure, Value}; 6 | 7 | use crate::dbus::dbusmenu_proxy::MenuLayout; 8 | 9 | /// A menu that should be displayed when clicking corresponding tray icon 10 | #[derive(Debug, Serialize, Clone)] 11 | pub struct TrayMenu { 12 | /// The unique identifier of the menu 13 | pub id: u32, 14 | /// A recursive list of submenus 15 | pub submenus: Vec, 16 | } 17 | 18 | /// Represent an entry in a menu as described in [com.canonical.dbusmenu](https://github.com/AyatanaIndicators/libdbusmenu/blob/4d03141aea4e2ad0f04ab73cf1d4f4bcc4a19f6c/libdbusmenu-glib/dbus-menu.xml#L75) 19 | /// This implementation currently support a sub section of the spec, if you feel something is missing don't hesitate to submit an issue. 20 | #[derive(Debug, Serialize, Clone)] 21 | pub struct MenuItem { 22 | /// Unique numeric id 23 | pub id: i32, 24 | /// If the menu item has children this property should be set to "submenu" 25 | pub children_display: Option, 26 | /// Text of the item, 27 | pub label: String, 28 | /// Whether the item can be activated or not. 29 | pub enabled: bool, 30 | /// True if the item is visible in the menu. 31 | pub visible: bool, 32 | /// Icon name of the item, following the freedesktop.org icon spec. 33 | pub icon_name: Option, 34 | /// Describe the current state of a "togglable" item. Can be one of: 35 | /// - Some(true): on 36 | /// - Some(false): off 37 | /// - None: indeterminate 38 | pub toggle_state: ToggleState, 39 | /// How the menuitem feels the information it's displaying to the 40 | /// user should be presented. 41 | pub toggle_type: ToggleType, 42 | /// Either a standard menu item or a separator [`MenuType`] 43 | pub menu_type: MenuType, 44 | /// How the menuitem feels the information it's displaying to the user should be presented. 45 | pub disposition: Disposition, 46 | /// A submenu for this item, typically this would ve revealed to the user by hovering the current item 47 | pub submenu: Vec, 48 | } 49 | 50 | impl Default for MenuItem { 51 | fn default() -> Self { 52 | Self { 53 | id: 0, 54 | children_display: None, 55 | label: "".to_string(), 56 | enabled: true, 57 | visible: true, 58 | icon_name: None, 59 | toggle_state: ToggleState::Indeterminate, 60 | toggle_type: ToggleType::CannotBeToggled, 61 | menu_type: MenuType::Standard, 62 | disposition: Disposition::Normal, 63 | submenu: vec![], 64 | } 65 | } 66 | } 67 | 68 | /// How the menuitem feels the information it's displaying to the 69 | /// user should be presented. 70 | #[derive(Debug, Serialize, Copy, Clone, Eq, PartialEq)] 71 | pub enum ToggleType { 72 | /// Item is an independent togglable item 73 | Checkmark, 74 | /// Item is part of a group where only one item can be 75 | /// toggled at a time 76 | Radio, 77 | /// Item cannot be toggled 78 | CannotBeToggled, 79 | } 80 | 81 | /// Either a standard menu item or a separator 82 | #[derive(Debug, Serialize, Copy, Clone, Eq, PartialEq)] 83 | pub enum MenuType { 84 | /// a separator 85 | Separator, 86 | /// an item which can be clicked to trigger an action or show another menu 87 | Standard, 88 | } 89 | 90 | /// How the menuitem feels the information it's displaying to the 91 | /// user should be presented. 92 | #[derive(Debug, Serialize, Copy, Clone, Eq, PartialEq)] 93 | pub enum Disposition { 94 | /// a standard menu item 95 | Normal, 96 | /// providing additional information to the user 97 | Informative, 98 | /// looking at potentially harmful results 99 | Warning, 100 | /// something bad could potentially happen 101 | Alert, 102 | } 103 | 104 | /// Describe the current state of a "togglable" item. 105 | #[derive(Debug, Serialize, Copy, Clone, Eq, PartialEq)] 106 | pub enum ToggleState { 107 | /// This item is toggled 108 | On, 109 | /// Item is not toggled 110 | Off, 111 | /// Item is not toggalble 112 | Indeterminate, 113 | } 114 | 115 | impl FromStr for MenuType { 116 | type Err = zbus::zvariant::Error; 117 | 118 | fn from_str(s: &str) -> Result { 119 | match s { 120 | "standard" => Ok(MenuType::Standard), 121 | "separator" => Ok(MenuType::Separator), 122 | _ => Err(zbus::zvariant::Error::IncorrectType), 123 | } 124 | } 125 | } 126 | 127 | impl FromStr for ToggleType { 128 | type Err = zbus::zvariant::Error; 129 | 130 | fn from_str(s: &str) -> Result { 131 | match s { 132 | "checkmark" => Ok(ToggleType::Checkmark), 133 | "radio" => Ok(ToggleType::Radio), 134 | _ => Err(zbus::zvariant::Error::IncorrectType), 135 | } 136 | } 137 | } 138 | 139 | impl FromStr for Disposition { 140 | type Err = zbus::zvariant::Error; 141 | 142 | fn from_str(s: &str) -> Result { 143 | match s { 144 | "normal" => Ok(Disposition::Normal), 145 | "informative" => Ok(Disposition::Informative), 146 | "warning" => Ok(Disposition::Warning), 147 | "alert" => Ok(Disposition::Alert), 148 | _ => Err(zbus::zvariant::Error::IncorrectType), 149 | } 150 | } 151 | } 152 | 153 | impl From for ToggleState { 154 | fn from(value: bool) -> Self { 155 | if value { 156 | ToggleState::On 157 | } else { 158 | ToggleState::Indeterminate 159 | } 160 | } 161 | } 162 | 163 | impl TryFrom for TrayMenu { 164 | type Error = zbus::zvariant::Error; 165 | 166 | fn try_from(value: MenuLayout) -> Result { 167 | let mut submenus = vec![]; 168 | for menu in &value.fields.submenus { 169 | let menu = MenuItem::try_from(menu)?; 170 | submenus.push(menu); 171 | } 172 | 173 | Ok(TrayMenu { 174 | id: value.id, 175 | submenus, 176 | }) 177 | } 178 | } 179 | 180 | impl TryFrom<&OwnedValue> for MenuItem { 181 | type Error = zbus::zvariant::Error; 182 | 183 | fn try_from(value: &OwnedValue) -> Result { 184 | let structure = value 185 | .downcast_ref::() 186 | .expect("Expected a layout"); 187 | 188 | let mut fields = structure.fields().iter(); 189 | let mut menu = MenuItem::default(); 190 | 191 | if let Some(Value::I32(id)) = fields.next() { 192 | menu.id = *id; 193 | } 194 | 195 | if let Some(Value::Dict(dict)) = fields.next() { 196 | menu.children_display = dict 197 | .get::("children_display")? 198 | .map(str::to_string); 199 | 200 | // see: https://github.com/AyatanaIndicators/libdbusmenu/blob/4d03141aea4e2ad0f04ab73cf1d4f4bcc4a19f6c/libdbusmenu-glib/dbus-menu.xml#L75 201 | menu.label = dict 202 | .get::("label")? 203 | .map(|label| label.replace('_', "")) 204 | .unwrap_or_default(); 205 | 206 | if let Some(enabled) = dict.get::("enabled")? { 207 | menu.enabled = *enabled 208 | } 209 | 210 | if let Some(visible) = dict.get::("visible")? { 211 | menu.visible = *visible; 212 | } 213 | 214 | menu.icon_name = dict.get::("icon-name")?.map(str::to_string); 215 | 216 | if let Some(disposition) = dict 217 | .get::("disposition") 218 | .ok() 219 | .flatten() 220 | .map(Disposition::from_str) 221 | .and_then(Result::ok) 222 | { 223 | menu.disposition = disposition; 224 | } 225 | 226 | menu.toggle_state = dict 227 | .get::("toggle-state") 228 | .ok() 229 | .flatten() 230 | .map(|value| ToggleState::from(*value)) 231 | .unwrap_or(ToggleState::Indeterminate); 232 | 233 | menu.toggle_type = dict 234 | .get::("toggle-type") 235 | .ok() 236 | .flatten() 237 | .map(ToggleType::from_str) 238 | .and_then(Result::ok) 239 | .unwrap_or(ToggleType::CannotBeToggled); 240 | 241 | menu.menu_type = dict 242 | .get::("type") 243 | .ok() 244 | .flatten() 245 | .map(MenuType::from_str) 246 | .and_then(Result::ok) 247 | .unwrap_or(MenuType::Standard); 248 | }; 249 | 250 | if let Some(Value::Array(array)) = fields.next() { 251 | let mut submenu = vec![]; 252 | for value in array.iter() { 253 | let value = OwnedValue::from(value); 254 | let menu = MenuItem::try_from(&value)?; 255 | submenu.push(menu); 256 | } 257 | 258 | menu.submenu = submenu; 259 | } 260 | 261 | Ok(menu) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /stray/src/message/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::message::menu::TrayMenu; 2 | use crate::message::tray::StatusNotifierItem; 3 | use serde::Serialize; 4 | 5 | /// Implementation of [com.canonical.dbusmenu](https://github.com/AyatanaIndicators/libdbusmenu/blob/4d03141aea4e2ad0f04ab73cf1d4f4bcc4a19f6c/libdbusmenu-glib/dbus-menu.xml#L75) 6 | pub mod menu; 7 | /// Implementation of [StatusNotifierItem](https://freedesktop.org/wiki/Specifications/StatusNotifierItem) 8 | pub mod tray; 9 | 10 | /// Messages send via by [`crate::SystemTray`] 11 | #[derive(Debug, Serialize, Clone)] 12 | pub enum NotifierItemMessage { 13 | /// Notify the state of an item along with its menu 14 | Update { 15 | /// The address of the NotifierItem on dbus, this will be required 16 | /// to request the activation of a manu entry via [`NotifierItemCommand::MenuItemClicked`] 17 | /// and remove the item when it is closed by the user. 18 | address: String, 19 | /// the status [`StatusNotifierItem`] and its metadata, to build a system tray ui 20 | /// the minimal would be to display it's icon and use it's menu address to send menu activation 21 | /// requests. 22 | item: Box, 23 | /// The menu layout of the item. 24 | menu: Option, 25 | }, 26 | /// A [`StatusNotifierItem`] has been removed from the tray 27 | Remove { 28 | /// The dbus address of the item, it serves as an unique identifier. 29 | address: String, 30 | }, 31 | } 32 | 33 | /// Command to send to a [`StatusNotifierItem`] 34 | #[derive(Debug)] 35 | pub enum NotifierItemCommand { 36 | /// Request activation of a menu item 37 | MenuItemClicked { 38 | /// Unique identifier of the item, see: [`crate::message::menu::MenuItem`] 39 | submenu_id: i32, 40 | /// DBus path of the menu item, (see: [`StatusNotifierItem`]) 41 | menu_path: String, 42 | /// Dbus address of the [`StatusNotifierItem`] 43 | notifier_address: String, 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /stray/src/message/tray.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::str::FromStr; 3 | 4 | use anyhow::anyhow; 5 | use serde::{Deserialize, Serialize}; 6 | use zbus::zvariant::{Array, ObjectPath, OwnedValue, Structure}; 7 | 8 | type DBusProperties = HashMap; 9 | 10 | struct PropsWrapper(DBusProperties); 11 | 12 | /// An Icon used for reporting the status of an application to the user or provide a quick access 13 | /// to common actions performed by that application. You can read the full specification at 14 | /// [freedesktop.org/wiki/Specifications/StatusNotifierItem](https://freedesktop.org/wiki/Specifications/StatusNotifierItem) 15 | /// or take a look at [the reference implementation](https://github.com/AyatanaIndicators/libayatana-appindicator/blob/c43a76e643ab930725d20d306bc3ca5e7874eebe/src/notification-item.xml) 16 | /// 17 | /// Note that this implementation is not feature complete. It only contains the minimal data 18 | /// needed to build a system tray and display tray menus. If you feel something important is 19 | /// should be added please reach out. 20 | #[derive(Serialize, Debug, Clone)] 21 | pub struct StatusNotifierItem { 22 | /// It's a name that should be unique for this application and consistent between sessions, 23 | /// such as the application name itself. 24 | pub id: String, 25 | /// Describes the category of this item. 26 | pub category: Category, 27 | /// Describes the status of this item or of the associated application. 28 | pub status: Status, 29 | /// The StatusNotifierItem can carry an icon that can be used by the visualization to identify the item. 30 | /// An icon can either be identified by its Freedesktop-compliant icon name, carried by 31 | /// this property of by the icon data itself, carried by the property IconPixmap. 32 | /// Visualizations are encouraged to prefer icon names over icon pixmaps if both are available 33 | pub icon_name: Option, 34 | /// Carries an ARGB32 binary representation of the icon, the format of icon data used in this specification 35 | /// is described in Section Icons 36 | pub icon_accessible_desc: Option, 37 | /// The Freedesktop-compliant name of an icon. this can be used by the visualization to indicate 38 | /// that the item is in RequestingAttention state. 39 | pub attention_icon_name: Option, 40 | /// It's a name that describes the application, it can be more descriptive than Id. 41 | pub title: Option, 42 | pub icon_theme_path: Option, 43 | pub icon_pixmap: Option>, 44 | /// DBus path to an object which should implement the com.canonical.dbusmenu interface 45 | /// This can be used to retrieve the wigdet menu via gtk/qt libdbusmenu implementation 46 | /// Instead of building it from the raw data 47 | pub menu: Option, 48 | } 49 | 50 | #[derive(Serialize, Debug, Clone)] 51 | #[serde(rename_all = "PascalCase")] 52 | pub enum Status { 53 | /// The item doesn't convey important information to the user, it can be considered an 54 | /// "idle" status and is likely that visualizations will chose to hide it. 55 | Passive, 56 | /// The item is active, is more important that the item will be shown in some way to the user. 57 | Active, 58 | } 59 | 60 | impl FromStr for Status { 61 | type Err = anyhow::Error; 62 | 63 | fn from_str(s: &str) -> Result { 64 | match s { 65 | "Passive" => Ok(Status::Active), 66 | "Active" => Ok(Status::Passive), 67 | other => Err(anyhow!( 68 | "Unknown 'Status' for status notifier item {}", 69 | other 70 | )), 71 | } 72 | } 73 | } 74 | 75 | /// Describes the category of this item. 76 | #[derive(Serialize, Debug, Clone)] 77 | #[serde(rename_all = "PascalCase")] 78 | pub enum Category { 79 | /// The item describes the status of a generic application, for instance the current state 80 | /// of a media player. In the case where the category of the item can not be known, such as 81 | /// when the item is being proxied from another incompatible or emulated system, 82 | /// ApplicationStatus can be used a sensible default fallback. 83 | ApplicationStatus, 84 | /// The item describes the status of communication oriented applications, like an instant 85 | /// messenger or an email client. 86 | Communications, 87 | /// The item describes services of the system not seen as a stand alone application by the user, 88 | /// such as an indicator for the activity of a disk indexing service. 89 | SystemServices, 90 | /// The item describes the state and control of a particular hardware, such as an indicator 91 | /// of the battery charge or sound card volume control. 92 | Hardware, 93 | } 94 | 95 | impl FromStr for Category { 96 | type Err = anyhow::Error; 97 | 98 | fn from_str(s: &str) -> Result { 99 | match s { 100 | "ApplicationStatus" => Ok(Category::ApplicationStatus), 101 | "Communications" => Ok(Category::Communications), 102 | "SystemServices" => Ok(Category::SystemServices), 103 | "Hardware" => Ok(Category::Hardware), 104 | other => Err(anyhow!( 105 | "Unknown 'Status' for status notifier item {}", 106 | other 107 | )), 108 | } 109 | } 110 | } 111 | 112 | #[derive(Deserialize, Serialize, Debug, Clone)] 113 | pub struct IconPixmap { 114 | pub width: i32, 115 | pub height: i32, 116 | pub pixels: Vec, 117 | } 118 | 119 | impl IconPixmap { 120 | fn from_array(a: &Array<'_>) -> Option> { 121 | let mut pixmaps = vec![]; 122 | 123 | a.iter().for_each(|b| { 124 | let s = b.downcast_ref::(); 125 | let fields = s.unwrap().fields(); 126 | let width = fields[0].downcast_ref::().unwrap(); 127 | let height = fields[1].downcast_ref::().unwrap(); 128 | let pixel_values = fields[2].downcast_ref::().unwrap().get(); 129 | let mut pixels = vec![]; 130 | pixel_values.iter().for_each(|p| { 131 | pixels.push(*p.downcast_ref::().unwrap()); 132 | }); 133 | pixmaps.push(IconPixmap { 134 | width: *width, 135 | height: *height, 136 | pixels, 137 | }) 138 | }); 139 | 140 | Some(pixmaps) 141 | } 142 | } 143 | 144 | impl TryFrom for StatusNotifierItem { 145 | type Error = anyhow::Error; 146 | fn try_from(props: HashMap) -> anyhow::Result { 147 | let props = PropsWrapper(props); 148 | match props.get_string("Id") { 149 | None => Err(anyhow!("StatusNotifier item should have an id")), 150 | Some(id) => Ok(StatusNotifierItem { 151 | id, 152 | title: props.get_string("Title"), 153 | category: props.get_category()?, 154 | icon_name: props.get_string("IconName"), 155 | status: props.get_status()?, 156 | icon_accessible_desc: props.get_string("IconAccessibleDesc"), 157 | attention_icon_name: props.get_string("AttentionIconName"), 158 | icon_theme_path: props.get_string("IconThemePath"), 159 | icon_pixmap: props.get_icon_pixmap(), 160 | menu: props.get_object_path("Menu"), 161 | }), 162 | } 163 | } 164 | } 165 | 166 | impl PropsWrapper { 167 | fn get_string(&self, key: &str) -> Option { 168 | self.0 169 | .get(key) 170 | .and_then(|value| value.downcast_ref::().map(|value| value.to_string())) 171 | } 172 | 173 | fn get_object_path(&self, key: &str) -> Option { 174 | self.0.get(key).and_then(|value| { 175 | value 176 | .downcast_ref::() 177 | .map(|value| value.to_string()) 178 | }) 179 | } 180 | 181 | fn get_category(&self) -> anyhow::Result { 182 | self.0 183 | .get("Category") 184 | .and_then(|value| value.downcast_ref::().map(Category::from_str)) 185 | .unwrap_or_else(|| Err(anyhow!("'Category' not found for item"))) 186 | } 187 | 188 | fn get_status(&self) -> anyhow::Result { 189 | self.0 190 | .get("Status") 191 | .and_then(|value| value.downcast_ref::().map(Status::from_str)) 192 | .unwrap_or_else(|| Err(anyhow!("'Status' not found for item"))) 193 | } 194 | 195 | fn get_icon_pixmap(&self) -> Option> { 196 | self.0 197 | .get("IconPixmap") 198 | .and_then(|value| value.downcast_ref::().map(IconPixmap::from_array)) 199 | .unwrap_or(None) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /stray/src/notifier_host/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::dbus::notifier_watcher_proxy::StatusNotifierWatcherProxy; 2 | use crate::error::{Result, StatusNotifierWatcherError}; 3 | use crate::{NotifierItemMessage, StatusNotifierWatcher}; 4 | use tokio::sync::broadcast; 5 | use zbus::{Connection, ConnectionBuilder}; 6 | 7 | pub struct NotifierHost { 8 | wellknown_name: String, 9 | rx: broadcast::Receiver, 10 | conn: Connection, 11 | } 12 | 13 | impl StatusNotifierWatcher { 14 | pub async fn create_notifier_host(&self, unique_id: &str) -> Result { 15 | let pid = std::process::id(); 16 | let id = &unique_id; 17 | let wellknown_name = format!("org.freedesktop.StatusNotifierHost-{pid}-{id}"); 18 | 19 | let conn = ConnectionBuilder::session()? 20 | .name(wellknown_name.as_str())? 21 | .build() 22 | .await?; 23 | 24 | let status_notifier_proxy = StatusNotifierWatcherProxy::new(&conn).await?; 25 | 26 | status_notifier_proxy 27 | .register_status_notifier_host(&wellknown_name) 28 | .await?; 29 | 30 | Ok(NotifierHost { 31 | wellknown_name, 32 | rx: self.tx.subscribe(), 33 | conn, 34 | }) 35 | } 36 | } 37 | 38 | impl NotifierHost { 39 | pub async fn recv(&mut self) -> Result { 40 | self.rx 41 | .recv() 42 | .await 43 | .map_err(StatusNotifierWatcherError::from) 44 | } 45 | 46 | /// This is used to drop the StatusNotifierHost and tell Dbus to release the name 47 | pub async fn destroy(self) -> Result<()> { 48 | let _ = self.conn.release_name(self.wellknown_name.as_str()).await?; 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stray/src/notifier_watcher/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::dbus::dbusmenu_proxy::DBusMenuProxy; 2 | use crate::dbus::notifier_item_proxy::StatusNotifierItemProxy; 3 | use crate::dbus::notifier_watcher_proxy::StatusNotifierWatcherProxy; 4 | use crate::error::Result; 5 | use crate::message::menu::TrayMenu; 6 | use crate::message::NotifierItemCommand; 7 | use crate::notifier_watcher::notifier_address::NotifierAddress; 8 | use crate::{ 9 | DbusNotifierWatcher, InterfaceName, MenuLayout, NotifierItemMessage, StatusNotifierItem, 10 | }; 11 | use tokio::sync::{broadcast, mpsc}; 12 | use tokio_stream::StreamExt; 13 | use zbus::fdo::PropertiesProxy; 14 | use zbus::{Connection, ConnectionBuilder}; 15 | 16 | pub(crate) mod notifier_address; 17 | 18 | /// Wrap the implementation of [org.freedesktop.StatusNotifierWatcher](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/) 19 | /// and [org.freedesktop.StatusNotifierHost](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierHost/). 20 | #[derive(Debug)] 21 | pub struct StatusNotifierWatcher { 22 | pub(crate) tx: broadcast::Sender, 23 | _rx: broadcast::Receiver, 24 | } 25 | 26 | impl StatusNotifierWatcher { 27 | /// Creates a new system stray and register a [StatusNotifierWatcher](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/) and [StatusNotifierHost](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierHost/) on dbus. 28 | /// Once created you can receive [`StatusNotifierItem`]. Once created you can start to poll message 29 | /// using the [`Stream`] implementation. 30 | pub async fn new(cmd_rx: mpsc::Receiver) -> Result { 31 | let (tx, rx) = broadcast::channel(5); 32 | 33 | { 34 | tracing::info!("Starting notifier watcher"); 35 | let tx = tx.clone(); 36 | 37 | tokio::spawn(async move { 38 | start_notifier_watcher(tx) 39 | .await 40 | .expect("Unexpected StatusNotifierError"); 41 | }); 42 | } 43 | 44 | tokio::spawn(async move { 45 | dispatch_ui_command(cmd_rx) 46 | .await 47 | .expect("Unexpected error while dispatching UI command"); 48 | }); 49 | 50 | Ok(StatusNotifierWatcher { tx, _rx: rx }) 51 | } 52 | } 53 | 54 | // Forward UI command to the Dbus menu proxy 55 | async fn dispatch_ui_command(mut cmd_rx: mpsc::Receiver) -> Result<()> { 56 | let connection = Connection::session().await?; 57 | 58 | while let Some(command) = cmd_rx.recv().await { 59 | match command { 60 | NotifierItemCommand::MenuItemClicked { 61 | submenu_id: id, 62 | menu_path, 63 | notifier_address, 64 | } => { 65 | let dbus_menu_proxy = DBusMenuProxy::builder(&connection) 66 | .destination(notifier_address) 67 | .unwrap() 68 | .path(menu_path) 69 | .unwrap() 70 | .build() 71 | .await?; 72 | 73 | dbus_menu_proxy 74 | .event( 75 | id, 76 | "clicked", 77 | &zbus::zvariant::Value::I32(32), 78 | chrono::offset::Local::now().timestamp_subsec_micros(), 79 | ) 80 | .await?; 81 | } 82 | } 83 | } 84 | 85 | Ok(()) 86 | } 87 | 88 | async fn start_notifier_watcher(sender: broadcast::Sender) -> Result<()> { 89 | let watcher = DbusNotifierWatcher::new(sender.clone()); 90 | 91 | let connection = ConnectionBuilder::session()? 92 | .name("org.kde.StatusNotifierWatcher")? 93 | .serve_at("/StatusNotifierWatcher", watcher)? 94 | .build() 95 | .await?; 96 | 97 | let status_notifier_removed = { 98 | let connection = connection.clone(); 99 | tokio::spawn(async move { 100 | status_notifier_removed_handle(connection).await?; 101 | Result::<()>::Ok(()) 102 | }) 103 | }; 104 | 105 | let status_notifier = 106 | tokio::spawn(async move { status_notifier_handle(connection, sender).await.unwrap() }); 107 | 108 | tokio::spawn(async move { 109 | let (r1, r2) = tokio::join!(status_notifier, status_notifier_removed,); 110 | if let Err(err) = r1 { 111 | tracing::error!("Status notifier error: {err:?}") 112 | } 113 | 114 | if let Err(err) = r2 { 115 | tracing::error!("Status notifier removed error: {err:?}") 116 | } 117 | }); 118 | 119 | Ok(()) 120 | } 121 | 122 | // Listen for 'NameOwnerChanged' on DBus whenever a service is removed 123 | // send 'UnregisterStatusNotifierItem' request to 'StatusNotifierWatcher' via dbus 124 | async fn status_notifier_removed_handle(connection: Connection) -> Result<()> { 125 | let dbus_proxy = zbus::fdo::DBusProxy::new(&connection).await.unwrap(); 126 | 127 | let mut changed = dbus_proxy 128 | .receive_name_owner_changed() 129 | .await 130 | .expect("fail to receive Dbus NameOwnerChanged"); 131 | 132 | while let Some(signal) = changed.next().await { 133 | let args = signal.args().expect("Failed to get signal args"); 134 | let old = args.old_owner(); 135 | let new = args.new_owner(); 136 | 137 | if old.is_some() && new.is_none() { 138 | let old_owner: String = old.as_ref().unwrap().to_string(); 139 | let watcher_proxy = StatusNotifierWatcherProxy::new(&connection) 140 | .await 141 | .expect("Failed to open StatusNotifierWatcherProxy"); 142 | 143 | if let Err(err) = watcher_proxy 144 | .unregister_status_notifier_item(&old_owner) 145 | .await 146 | { 147 | tracing::error!("Failed to unregister status notifier: {err:?}") 148 | } 149 | } 150 | } 151 | 152 | Ok(()) 153 | } 154 | 155 | // 1. Start StatusNotifierHost on DBus 156 | // 2. Query already registered StatusNotifier, call GetAll to update the UI and listen for property changes via Dbus.PropertiesChanged 157 | // 3. subscribe to StatusNotifierWatcher.RegisteredStatusNotifierItems 158 | // 4. Whenever a new notifier is registered repeat steps 2 159 | // FIXME : Move this to HOST 160 | async fn status_notifier_handle( 161 | connection: Connection, 162 | sender: broadcast::Sender, 163 | ) -> Result<()> { 164 | let status_notifier_proxy = StatusNotifierWatcherProxy::new(&connection).await?; 165 | 166 | let notifier_items: Vec = status_notifier_proxy 167 | .registered_status_notifier_items() 168 | .await?; 169 | 170 | tracing::info!("Got {} notifier items", notifier_items.len()); 171 | 172 | // Start watching for all registered notifier items 173 | for service in notifier_items.iter() { 174 | let service = NotifierAddress::from_notifier_service(service); 175 | if let Ok(notifier_address) = service { 176 | let connection = connection.clone(); 177 | let sender = sender.clone(); 178 | watch_notifier_props(notifier_address, connection, sender).await?; 179 | } 180 | } 181 | 182 | // Listen for new notifier items 183 | let mut new_notifier = status_notifier_proxy 184 | .receive_status_notifier_item_registered() 185 | .await?; 186 | 187 | while let Some(notifier) = new_notifier.next().await { 188 | let args = notifier.args()?; 189 | let service: &str = args.service(); 190 | tracing::info!( 191 | "StatusNotifierItemRegistered signal received service={}", 192 | service 193 | ); 194 | 195 | let service = NotifierAddress::from_notifier_service(service); 196 | if let Ok(notifier_address) = service { 197 | let connection = connection.clone(); 198 | let sender = sender.clone(); 199 | tokio::spawn(async move { 200 | watch_notifier_props(notifier_address, connection, sender).await?; 201 | Result::<()>::Ok(()) 202 | }); 203 | } 204 | } 205 | 206 | Ok(()) 207 | } 208 | 209 | // Listen for PropertiesChanged on DBus and send an update request on change 210 | async fn watch_notifier_props( 211 | address_parts: NotifierAddress, 212 | connection: Connection, 213 | sender: broadcast::Sender, 214 | ) -> Result<()> { 215 | tokio::spawn(async move { 216 | // Connect to DBus.Properties 217 | let dbus_properties_proxy = zbus::fdo::PropertiesProxy::builder(&connection) 218 | .destination(address_parts.destination.as_str())? 219 | .path(address_parts.path.as_str())? 220 | .build() 221 | .await?; 222 | 223 | // call Properties.GetAll once and send an update to the UI 224 | fetch_properties_and_update( 225 | sender.clone(), 226 | &dbus_properties_proxy, 227 | address_parts.destination.clone(), 228 | connection.clone(), 229 | ) 230 | .await?; 231 | 232 | // Connect to the notifier proxy to watch for properties change 233 | let notifier_item_proxy = StatusNotifierItemProxy::builder(&connection) 234 | .destination(address_parts.destination.as_str())? 235 | .path(address_parts.path.as_str())? 236 | .build() 237 | .await?; 238 | 239 | let mut props_changed = notifier_item_proxy.receive_all_signals().await?; 240 | 241 | // Whenever a property change query all props and update the UI 242 | while props_changed.next().await.is_some() { 243 | fetch_properties_and_update( 244 | sender.clone(), 245 | &dbus_properties_proxy, 246 | address_parts.destination.clone(), 247 | connection.clone(), 248 | ) 249 | .await?; 250 | } 251 | 252 | Result::<()>::Ok(()) 253 | }); 254 | 255 | Ok(()) 256 | } 257 | 258 | // Fetch Properties from DBus proxy and send an update to the UI channel 259 | async fn fetch_properties_and_update( 260 | sender: broadcast::Sender, 261 | dbus_properties_proxy: &PropertiesProxy<'_>, 262 | item_address: String, 263 | connection: Connection, 264 | ) -> Result<()> { 265 | let interface = InterfaceName::from_static_str("org.kde.StatusNotifierItem")?; 266 | let props = dbus_properties_proxy.get_all(interface).await?; 267 | let item = StatusNotifierItem::try_from(props); 268 | 269 | // Only send item that maps correctly to our internal StatusNotifierItem representation 270 | if let Ok(item) = item { 271 | let menu = match &item.menu { 272 | None => None, 273 | Some(menu_address) => watch_menu( 274 | item_address.clone(), 275 | item.clone(), 276 | connection.clone(), 277 | menu_address.clone(), 278 | sender.clone(), 279 | ) 280 | .await 281 | .ok(), 282 | }; 283 | 284 | tracing::info!("StatusNotifierItem updated, dbus-address={item_address}"); 285 | 286 | sender 287 | .send(NotifierItemMessage::Update { 288 | address: item_address.to_string(), 289 | item: Box::new(item), 290 | menu, 291 | }) 292 | .expect("Failed to dispatch NotifierItemMessage"); 293 | } 294 | 295 | Ok(()) 296 | } 297 | 298 | async fn watch_menu( 299 | item_address: String, 300 | item: StatusNotifierItem, 301 | connection: Connection, 302 | menu_address: String, 303 | sender: broadcast::Sender, 304 | ) -> Result { 305 | let dbus_menu_proxy = DBusMenuProxy::builder(&connection) 306 | .destination(item_address.as_str())? 307 | .path(menu_address.as_str())? 308 | .build() 309 | .await?; 310 | 311 | let menu: MenuLayout = dbus_menu_proxy.get_layout(0, 10, &[]).await.unwrap(); 312 | 313 | tokio::spawn(async move { 314 | let dbus_menu_proxy = DBusMenuProxy::builder(&connection) 315 | .destination(item_address.as_str())? 316 | .path(menu_address.as_str())? 317 | .build() 318 | .await?; 319 | 320 | let mut props_changed = dbus_menu_proxy.receive_all_signals().await?; 321 | 322 | while props_changed.next().await.is_some() { 323 | let menu: MenuLayout = dbus_menu_proxy.get_layout(0, 10, &[]).await.unwrap(); 324 | let menu = TrayMenu::try_from(menu).ok(); 325 | sender.send(NotifierItemMessage::Update { 326 | address: item_address.to_string(), 327 | item: Box::new(item.clone()), 328 | menu, 329 | })?; 330 | } 331 | anyhow::Result::<(), anyhow::Error>::Ok(()) 332 | }); 333 | 334 | TrayMenu::try_from(menu).map_err(Into::into) 335 | } 336 | -------------------------------------------------------------------------------- /stray/src/notifier_watcher/notifier_address.rs: -------------------------------------------------------------------------------- 1 | use crate::error; 2 | use crate::error::StatusNotifierWatcherError; 3 | 4 | // A helper to convert RegisterStatusNotifier calls to 5 | // StatusNotifier address parts 6 | #[derive(Debug)] 7 | pub(crate) struct NotifierAddress { 8 | // Notifier destination on the bus, ex: ":1.522" 9 | pub(crate) destination: String, 10 | // The notifier object path, ex: "/org/ayatana/NotificationItem/Element1" 11 | pub(crate) path: String, 12 | } 13 | 14 | impl NotifierAddress { 15 | pub(crate) fn from_notifier_service(service: &str) -> error::Result { 16 | if let Some((destination, path)) = service.split_once('/') { 17 | Ok(NotifierAddress { 18 | destination: destination.to_string(), 19 | path: format!("/{}", path), 20 | }) 21 | } else if service.starts_with(':') { 22 | Ok(NotifierAddress { 23 | destination: service[0..6].to_string(), 24 | path: "/StatusNotifierItem".to_string(), 25 | }) 26 | } else { 27 | Err(StatusNotifierWatcherError::DbusAddressError( 28 | service.to_string(), 29 | )) 30 | } 31 | } 32 | } 33 | --------------------------------------------------------------------------------