├── .github └── workflows │ └── devskim.yml ├── .gitignore ├── ALIASES.md ├── CONTRIB.md ├── Cargo.lock ├── Cargo.toml ├── DUAL-CONFIG.md ├── FAQ.md ├── FEATURES.md ├── LICENSE ├── PRESETS.md ├── README.md ├── VARIABLES.md ├── WIDGETS.md ├── examples ├── config.json └── style.css ├── resources └── cava_tmp.conf └── src ├── config.rs ├── constants.rs ├── loop.rs ├── macros.rs ├── main.rs ├── structures.rs ├── ui.rs ├── utils ├── aliases.rs ├── cava.rs ├── environment.rs ├── hyprland.rs └── mod.rs ├── widget.rs └── widgets ├── box_widget.rs ├── button_widget.rs ├── cava_widget.rs ├── label_widget.rs ├── mod.rs ├── spacing_widget.rs └── tray_widget.rs /.github/workflows/devskim.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: DevSkim 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | pull_request: 12 | branches: [ "main" ] 13 | schedule: 14 | - cron: '24 5 * * 3' 15 | 16 | jobs: 17 | lint: 18 | name: DevSkim 19 | runs-on: ubuntu-20.04 20 | permissions: 21 | actions: read 22 | contents: read 23 | security-events: write 24 | steps: 25 | - name: Checkout code 26 | uses: actions/checkout@v3 27 | 28 | - name: Run DevSkim scanner 29 | uses: microsoft/DevSkim-Action@v1 30 | 31 | - name: Upload DevSkim scan results to GitHub Security tab 32 | uses: github/codeql-action/upload-sarif@v2 33 | with: 34 | sarif_file: devskim-results.sarif 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore .fleet in case others use it. 2 | .fleet/ 3 | 4 | # Generated by Cargo 5 | # will have compiled files and executables 6 | /target/ 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | -------------------------------------------------------------------------------- /ALIASES.md: -------------------------------------------------------------------------------- 1 | # Aliases 2 | > **Warning**: 3 | > Aliases relies mainly the presence of `/etc/os-release`, which may or may not be available on all distributions. 4 | > 5 | > If `lxinfo` (the library used) fails to retrieve any information, then aliases will be disabled. 6 | *** 7 | These aliases are only valid for commands (+ tooltip commands). 8 | *** 9 | - `%username%` - Username 10 | - `%hostname%` - Hostname 11 | - `%shell%` - Active Session Shell 12 | - `%distro%` - Distribution name 13 | - `%distro_id%` - Distribution ID, for example: `arch` 14 | - `%distro_build_id%` - Distribution Build ID, for example `rolling` 15 | - `%total_mem%` - Total amount of installed memory (in GB) 16 | - `%cached_mem%` - Total amount of cached memory (in GB) 17 | - `%available_mem%` - Total amount of available memory (in GB) 18 | - `%used_mem%` - Total amount of used memory (in GB) 19 | - `%hl_workspace%` - Current Hyprland Workspace ID 20 | - `%hl_window%` - Current Hyprland Window Title 21 | 22 | ## I can just use `whoami`, why all of this? 23 | You may use completely dynamic commands like `whoami` if you want, the benefit with using aliases are: 24 | 25 | 1. Lower overhead due to being retrieved via `libc`, rather than expensive commands 26 | 2. A lot faster to process 27 | 3. Cached at startup, then reused afterwards - **To be changed since it's a design flaw** 28 | -------------------------------------------------------------------------------- /CONTRIB.md: -------------------------------------------------------------------------------- 1 | # Contribution 2 | ## Style 3 | Look around the project to get a view on how the code is structured, then adapt to that. 4 | 5 | Also make sure you format your code accordingly. The config I use for all my Rust projects is just vanilla LSP with Rust. Config is [here](https://github.com/vars1ty/NeoRS). 6 | 7 | ## Error Handling 8 | If the possibility of an error happening is super low (like never), then you may use `unwrap()`. An example of this can be seen with variables created via lazy_static!. 9 | 10 | If it may happen frequently however, then please use `expect()` / `unwrap_or...`. Or if it makes sense; guard the logic and try to make it not crash the application. 11 | 12 | ## Caching 13 | If you are just accessing a value once or twice upon startup and **it's not heavy to do so**; don't cache it. 14 | 15 | On the other hand, if it's heavy (an example of this could be seen at issue #13) - Try and cache what you are accessing and go from there. 16 | 17 | ## Widgets 18 | When adding widgets, make sure you follow the relatively simple widget structure that already exists. 19 | 20 | Also make sure you document your widgets behavior, keys (if any), etc. 21 | 22 | ## Breaking Changes 23 | If your commit contains breaking changes, or ones that are unstable, please use the `new-release` branch. 24 | 25 | ### Why? 26 | Because `new-release` isn't cloned when the user uses `hybrid-bar-git`, therefore making it a better spot for testing. 27 | 28 | ## I found some bad code, can I redo it? 29 | You may submit a PR with **your** new code, just make sure it's easily readable and doesn't have worse performance than the original code. 30 | -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "android-tzdata" 31 | version = "0.1.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 | 35 | [[package]] 36 | name = "android_system_properties" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 | dependencies = [ 41 | "libc", 42 | ] 43 | 44 | [[package]] 45 | name = "anyhow" 46 | version = "1.0.82" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" 49 | 50 | [[package]] 51 | name = "async-broadcast" 52 | version = "0.4.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61" 55 | dependencies = [ 56 | "event-listener", 57 | "futures-core", 58 | "parking_lot", 59 | ] 60 | 61 | [[package]] 62 | name = "async-channel" 63 | version = "1.9.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 66 | dependencies = [ 67 | "concurrent-queue", 68 | "event-listener", 69 | "futures-core", 70 | ] 71 | 72 | [[package]] 73 | name = "async-executor" 74 | version = "1.11.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" 77 | dependencies = [ 78 | "async-task", 79 | "concurrent-queue", 80 | "fastrand", 81 | "futures-lite", 82 | "slab", 83 | ] 84 | 85 | [[package]] 86 | name = "async-lock" 87 | version = "2.8.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" 90 | dependencies = [ 91 | "event-listener", 92 | ] 93 | 94 | [[package]] 95 | name = "async-recursion" 96 | version = "0.3.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" 99 | dependencies = [ 100 | "proc-macro2", 101 | "quote", 102 | "syn 1.0.109", 103 | ] 104 | 105 | [[package]] 106 | name = "async-task" 107 | version = "4.7.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 110 | 111 | [[package]] 112 | name = "async-trait" 113 | version = "0.1.80" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" 116 | dependencies = [ 117 | "proc-macro2", 118 | "quote", 119 | "syn 2.0.60", 120 | ] 121 | 122 | [[package]] 123 | name = "atk" 124 | version = "0.17.1" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" 127 | dependencies = [ 128 | "atk-sys", 129 | "bitflags 1.3.2", 130 | "glib", 131 | "libc", 132 | ] 133 | 134 | [[package]] 135 | name = "atk-sys" 136 | version = "0.17.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "e3bf0a7ca572fbd5762fd8f8cd65a581e06767bc1234913fe1f43e370cff6e90" 139 | dependencies = [ 140 | "glib-sys", 141 | "gobject-sys", 142 | "libc", 143 | "system-deps", 144 | ] 145 | 146 | [[package]] 147 | name = "autocfg" 148 | version = "1.2.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" 151 | 152 | [[package]] 153 | name = "backtrace" 154 | version = "0.3.71" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 157 | dependencies = [ 158 | "addr2line", 159 | "cc", 160 | "cfg-if", 161 | "libc", 162 | "miniz_oxide", 163 | "object", 164 | "rustc-demangle", 165 | ] 166 | 167 | [[package]] 168 | name = "bitflags" 169 | version = "1.3.2" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 172 | 173 | [[package]] 174 | name = "bitflags" 175 | version = "2.5.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 178 | 179 | [[package]] 180 | name = "bumpalo" 181 | version = "3.16.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 184 | 185 | [[package]] 186 | name = "byte-unit" 187 | version = "4.0.19" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" 190 | dependencies = [ 191 | "serde", 192 | "utf8-width", 193 | ] 194 | 195 | [[package]] 196 | name = "byteorder" 197 | version = "1.5.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 200 | 201 | [[package]] 202 | name = "bytes" 203 | version = "1.6.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 206 | 207 | [[package]] 208 | name = "cairo-rs" 209 | version = "0.17.10" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" 212 | dependencies = [ 213 | "bitflags 1.3.2", 214 | "cairo-sys-rs", 215 | "glib", 216 | "libc", 217 | "once_cell", 218 | "thiserror", 219 | ] 220 | 221 | [[package]] 222 | name = "cairo-sys-rs" 223 | version = "0.17.10" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "691d0c66b1fb4881be80a760cb8fe76ea97218312f9dfe2c9cc0f496ca279cb1" 226 | dependencies = [ 227 | "glib-sys", 228 | "libc", 229 | "system-deps", 230 | ] 231 | 232 | [[package]] 233 | name = "cc" 234 | version = "1.0.96" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" 237 | 238 | [[package]] 239 | name = "cfg-expr" 240 | version = "0.15.8" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" 243 | dependencies = [ 244 | "smallvec", 245 | "target-lexicon", 246 | ] 247 | 248 | [[package]] 249 | name = "cfg-if" 250 | version = "1.0.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 253 | 254 | [[package]] 255 | name = "chrono" 256 | version = "0.4.38" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 259 | dependencies = [ 260 | "android-tzdata", 261 | "iana-time-zone", 262 | "js-sys", 263 | "num-traits", 264 | "wasm-bindgen", 265 | "windows-targets 0.52.5", 266 | ] 267 | 268 | [[package]] 269 | name = "concurrent-queue" 270 | version = "2.5.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 273 | dependencies = [ 274 | "crossbeam-utils", 275 | ] 276 | 277 | [[package]] 278 | name = "convert_case" 279 | version = "0.4.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 282 | 283 | [[package]] 284 | name = "core-foundation-sys" 285 | version = "0.8.6" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 288 | 289 | [[package]] 290 | name = "crossbeam-utils" 291 | version = "0.8.19" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 294 | 295 | [[package]] 296 | name = "derivative" 297 | version = "2.2.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 300 | dependencies = [ 301 | "proc-macro2", 302 | "quote", 303 | "syn 1.0.109", 304 | ] 305 | 306 | [[package]] 307 | name = "derive_more" 308 | version = "0.99.17" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 311 | dependencies = [ 312 | "convert_case", 313 | "proc-macro2", 314 | "quote", 315 | "rustc_version", 316 | "syn 1.0.109", 317 | ] 318 | 319 | [[package]] 320 | name = "dirs" 321 | version = "4.0.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 324 | dependencies = [ 325 | "dirs-sys", 326 | ] 327 | 328 | [[package]] 329 | name = "dirs-sys" 330 | version = "0.3.7" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 333 | dependencies = [ 334 | "libc", 335 | "redox_users", 336 | "winapi", 337 | ] 338 | 339 | [[package]] 340 | name = "doc-comment" 341 | version = "0.3.3" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 344 | 345 | [[package]] 346 | name = "enumflags2" 347 | version = "0.7.9" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" 350 | dependencies = [ 351 | "enumflags2_derive", 352 | "serde", 353 | ] 354 | 355 | [[package]] 356 | name = "enumflags2_derive" 357 | version = "0.7.9" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" 360 | dependencies = [ 361 | "proc-macro2", 362 | "quote", 363 | "syn 2.0.60", 364 | ] 365 | 366 | [[package]] 367 | name = "equivalent" 368 | version = "1.0.1" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 371 | 372 | [[package]] 373 | name = "errno" 374 | version = "0.3.8" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 377 | dependencies = [ 378 | "libc", 379 | "windows-sys 0.52.0", 380 | ] 381 | 382 | [[package]] 383 | name = "event-listener" 384 | version = "2.5.3" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 387 | 388 | [[package]] 389 | name = "fastrand" 390 | version = "2.1.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" 393 | 394 | [[package]] 395 | name = "field-offset" 396 | version = "0.3.6" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" 399 | dependencies = [ 400 | "memoffset 0.9.1", 401 | "rustc_version", 402 | ] 403 | 404 | [[package]] 405 | name = "futures" 406 | version = "0.3.30" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 409 | dependencies = [ 410 | "futures-channel", 411 | "futures-core", 412 | "futures-executor", 413 | "futures-io", 414 | "futures-sink", 415 | "futures-task", 416 | "futures-util", 417 | ] 418 | 419 | [[package]] 420 | name = "futures-channel" 421 | version = "0.3.30" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 424 | dependencies = [ 425 | "futures-core", 426 | "futures-sink", 427 | ] 428 | 429 | [[package]] 430 | name = "futures-core" 431 | version = "0.3.30" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 434 | 435 | [[package]] 436 | name = "futures-executor" 437 | version = "0.3.30" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 440 | dependencies = [ 441 | "futures-core", 442 | "futures-task", 443 | "futures-util", 444 | ] 445 | 446 | [[package]] 447 | name = "futures-io" 448 | version = "0.3.30" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 451 | 452 | [[package]] 453 | name = "futures-lite" 454 | version = "2.3.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" 457 | dependencies = [ 458 | "fastrand", 459 | "futures-core", 460 | "futures-io", 461 | "parking", 462 | "pin-project-lite", 463 | ] 464 | 465 | [[package]] 466 | name = "futures-macro" 467 | version = "0.3.30" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 470 | dependencies = [ 471 | "proc-macro2", 472 | "quote", 473 | "syn 2.0.60", 474 | ] 475 | 476 | [[package]] 477 | name = "futures-sink" 478 | version = "0.3.30" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 481 | 482 | [[package]] 483 | name = "futures-task" 484 | version = "0.3.30" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 487 | 488 | [[package]] 489 | name = "futures-util" 490 | version = "0.3.30" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 493 | dependencies = [ 494 | "futures-channel", 495 | "futures-core", 496 | "futures-io", 497 | "futures-macro", 498 | "futures-sink", 499 | "futures-task", 500 | "memchr", 501 | "pin-project-lite", 502 | "pin-utils", 503 | "slab", 504 | ] 505 | 506 | [[package]] 507 | name = "gdk" 508 | version = "0.17.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" 511 | dependencies = [ 512 | "bitflags 1.3.2", 513 | "cairo-rs", 514 | "gdk-pixbuf", 515 | "gdk-sys", 516 | "gio", 517 | "glib", 518 | "libc", 519 | "pango", 520 | ] 521 | 522 | [[package]] 523 | name = "gdk-pixbuf" 524 | version = "0.17.10" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "695d6bc846438c5708b07007537b9274d883373dd30858ca881d7d71b5540717" 527 | dependencies = [ 528 | "bitflags 1.3.2", 529 | "gdk-pixbuf-sys", 530 | "gio", 531 | "glib", 532 | "libc", 533 | "once_cell", 534 | ] 535 | 536 | [[package]] 537 | name = "gdk-pixbuf-sys" 538 | version = "0.17.10" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "9285ec3c113c66d7d0ab5676599176f1f42f4944ca1b581852215bf5694870cb" 541 | dependencies = [ 542 | "gio-sys", 543 | "glib-sys", 544 | "gobject-sys", 545 | "libc", 546 | "system-deps", 547 | ] 548 | 549 | [[package]] 550 | name = "gdk-sys" 551 | version = "0.17.0" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "2152de9d38bc67a17b3fe49dc0823af5bf874df59ea088c5f28f31cf103de703" 554 | dependencies = [ 555 | "cairo-sys-rs", 556 | "gdk-pixbuf-sys", 557 | "gio-sys", 558 | "glib-sys", 559 | "gobject-sys", 560 | "libc", 561 | "pango-sys", 562 | "pkg-config", 563 | "system-deps", 564 | ] 565 | 566 | [[package]] 567 | name = "getrandom" 568 | version = "0.2.14" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" 571 | dependencies = [ 572 | "cfg-if", 573 | "libc", 574 | "wasi", 575 | ] 576 | 577 | [[package]] 578 | name = "gimli" 579 | version = "0.28.1" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 582 | 583 | [[package]] 584 | name = "gio" 585 | version = "0.17.10" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "a6973e92937cf98689b6a054a9e56c657ed4ff76de925e36fc331a15f0c5d30a" 588 | dependencies = [ 589 | "bitflags 1.3.2", 590 | "futures-channel", 591 | "futures-core", 592 | "futures-io", 593 | "futures-util", 594 | "gio-sys", 595 | "glib", 596 | "libc", 597 | "once_cell", 598 | "pin-project-lite", 599 | "smallvec", 600 | "thiserror", 601 | ] 602 | 603 | [[package]] 604 | name = "gio-sys" 605 | version = "0.17.10" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "0ccf87c30a12c469b6d958950f6a9c09f2be20b7773f7e70d20b867fdf2628c3" 608 | dependencies = [ 609 | "glib-sys", 610 | "gobject-sys", 611 | "libc", 612 | "system-deps", 613 | "winapi", 614 | ] 615 | 616 | [[package]] 617 | name = "glib" 618 | version = "0.17.10" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "d3fad45ba8d4d2cea612b432717e834f48031cd8853c8aaf43b2c79fec8d144b" 621 | dependencies = [ 622 | "bitflags 1.3.2", 623 | "futures-channel", 624 | "futures-core", 625 | "futures-executor", 626 | "futures-task", 627 | "futures-util", 628 | "gio-sys", 629 | "glib-macros", 630 | "glib-sys", 631 | "gobject-sys", 632 | "libc", 633 | "memchr", 634 | "once_cell", 635 | "smallvec", 636 | "thiserror", 637 | ] 638 | 639 | [[package]] 640 | name = "glib-macros" 641 | version = "0.17.10" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26" 644 | dependencies = [ 645 | "anyhow", 646 | "heck 0.4.1", 647 | "proc-macro-crate", 648 | "proc-macro-error", 649 | "proc-macro2", 650 | "quote", 651 | "syn 1.0.109", 652 | ] 653 | 654 | [[package]] 655 | name = "glib-sys" 656 | version = "0.17.10" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "d80aa6ea7bba0baac79222204aa786a6293078c210abe69ef1336911d4bdc4f0" 659 | dependencies = [ 660 | "libc", 661 | "system-deps", 662 | ] 663 | 664 | [[package]] 665 | name = "gobject-sys" 666 | version = "0.17.10" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "cd34c3317740a6358ec04572c1bcfd3ac0b5b6529275fae255b237b314bb8062" 669 | dependencies = [ 670 | "glib-sys", 671 | "libc", 672 | "system-deps", 673 | ] 674 | 675 | [[package]] 676 | name = "gtk" 677 | version = "0.17.1" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" 680 | dependencies = [ 681 | "atk", 682 | "bitflags 1.3.2", 683 | "cairo-rs", 684 | "field-offset", 685 | "futures-channel", 686 | "gdk", 687 | "gdk-pixbuf", 688 | "gio", 689 | "glib", 690 | "gtk-sys", 691 | "gtk3-macros", 692 | "libc", 693 | "once_cell", 694 | "pango", 695 | "pkg-config", 696 | ] 697 | 698 | [[package]] 699 | name = "gtk-layer-shell" 700 | version = "0.6.1" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "992f5fedb31835424a5280acd162bf348995f617d26969fde8d3dfd389b3ff5f" 703 | dependencies = [ 704 | "bitflags 2.5.0", 705 | "gdk", 706 | "glib", 707 | "glib-sys", 708 | "gtk", 709 | "gtk-layer-shell-sys", 710 | "libc", 711 | ] 712 | 713 | [[package]] 714 | name = "gtk-layer-shell-sys" 715 | version = "0.6.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "5754bcfaadfc3529116af6ae93559b267d88647f965382153a4b8ea9372be75a" 718 | dependencies = [ 719 | "gdk-sys", 720 | "glib-sys", 721 | "gtk-sys", 722 | "libc", 723 | "system-deps", 724 | ] 725 | 726 | [[package]] 727 | name = "gtk-sys" 728 | version = "0.17.0" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "4d8eb6a4b93e5a7e6980f7348d08c1cd93d31fae07cf97f20678c5ec41de3d7e" 731 | dependencies = [ 732 | "atk-sys", 733 | "cairo-sys-rs", 734 | "gdk-pixbuf-sys", 735 | "gdk-sys", 736 | "gio-sys", 737 | "glib-sys", 738 | "gobject-sys", 739 | "libc", 740 | "pango-sys", 741 | "system-deps", 742 | ] 743 | 744 | [[package]] 745 | name = "gtk3-macros" 746 | version = "0.17.1" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "3efb84d682c9a39c10bd9f24f5a4b9c15cc8c7edc45c19cb2ca2c4fc38b2d95e" 749 | dependencies = [ 750 | "anyhow", 751 | "proc-macro-crate", 752 | "proc-macro-error", 753 | "proc-macro2", 754 | "quote", 755 | "syn 1.0.109", 756 | ] 757 | 758 | [[package]] 759 | name = "hashbrown" 760 | version = "0.14.5" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 763 | 764 | [[package]] 765 | name = "heck" 766 | version = "0.4.1" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 769 | 770 | [[package]] 771 | name = "heck" 772 | version = "0.5.0" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 775 | 776 | [[package]] 777 | name = "hermit-abi" 778 | version = "0.3.9" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 781 | 782 | [[package]] 783 | name = "hex" 784 | version = "0.4.3" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 787 | 788 | [[package]] 789 | name = "hybrid-bar" 790 | version = "0.4.9" 791 | dependencies = [ 792 | "glib", 793 | "gtk", 794 | "gtk-layer-shell", 795 | "hyprland", 796 | "json", 797 | "lazy_static", 798 | "lxinfo", 799 | "stray", 800 | "tokio", 801 | "tracing-subscriber", 802 | ] 803 | 804 | [[package]] 805 | name = "hyprland" 806 | version = "0.3.13" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "f87a8f1cc065d451894dd3916c0bc3fcf9b67b276126c05f27b1db912688dde8" 809 | dependencies = [ 810 | "async-trait", 811 | "derive_more", 812 | "doc-comment", 813 | "futures", 814 | "hex", 815 | "hyprland-macros", 816 | "lazy_static", 817 | "num-traits", 818 | "paste", 819 | "regex", 820 | "serde", 821 | "serde_json", 822 | "serde_repr", 823 | "strum", 824 | "tokio", 825 | ] 826 | 827 | [[package]] 828 | name = "hyprland-macros" 829 | version = "0.3.4" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "c941d3d52e979612af8cb94e8de49000c7fada2014a7791d173ab41339f4e4eb" 832 | dependencies = [ 833 | "quote", 834 | "syn 2.0.60", 835 | ] 836 | 837 | [[package]] 838 | name = "iana-time-zone" 839 | version = "0.1.60" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 842 | dependencies = [ 843 | "android_system_properties", 844 | "core-foundation-sys", 845 | "iana-time-zone-haiku", 846 | "js-sys", 847 | "wasm-bindgen", 848 | "windows-core", 849 | ] 850 | 851 | [[package]] 852 | name = "iana-time-zone-haiku" 853 | version = "0.1.2" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 856 | dependencies = [ 857 | "cc", 858 | ] 859 | 860 | [[package]] 861 | name = "indexmap" 862 | version = "2.2.6" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 865 | dependencies = [ 866 | "equivalent", 867 | "hashbrown", 868 | ] 869 | 870 | [[package]] 871 | name = "itoa" 872 | version = "1.0.11" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 875 | 876 | [[package]] 877 | name = "js-sys" 878 | version = "0.3.69" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 881 | dependencies = [ 882 | "wasm-bindgen", 883 | ] 884 | 885 | [[package]] 886 | name = "json" 887 | version = "0.12.4" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" 890 | 891 | [[package]] 892 | name = "lazy_static" 893 | version = "1.4.0" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 896 | 897 | [[package]] 898 | name = "libc" 899 | version = "0.2.154" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" 902 | 903 | [[package]] 904 | name = "libredox" 905 | version = "0.1.3" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 908 | dependencies = [ 909 | "bitflags 2.5.0", 910 | "libc", 911 | ] 912 | 913 | [[package]] 914 | name = "linux-raw-sys" 915 | version = "0.4.13" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 918 | 919 | [[package]] 920 | name = "lock_api" 921 | version = "0.4.12" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 924 | dependencies = [ 925 | "autocfg", 926 | "scopeguard", 927 | ] 928 | 929 | [[package]] 930 | name = "log" 931 | version = "0.4.21" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 934 | 935 | [[package]] 936 | name = "lxinfo" 937 | version = "0.1.2" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "efee7e5570518c86be0777caf63a7b1231061c155458130b5fc0e7a2ca35cd95" 940 | dependencies = [ 941 | "byte-unit", 942 | "libc", 943 | ] 944 | 945 | [[package]] 946 | name = "memchr" 947 | version = "2.7.2" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 950 | 951 | [[package]] 952 | name = "memoffset" 953 | version = "0.6.5" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 956 | dependencies = [ 957 | "autocfg", 958 | ] 959 | 960 | [[package]] 961 | name = "memoffset" 962 | version = "0.9.1" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 965 | dependencies = [ 966 | "autocfg", 967 | ] 968 | 969 | [[package]] 970 | name = "miniz_oxide" 971 | version = "0.7.2" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 974 | dependencies = [ 975 | "adler", 976 | ] 977 | 978 | [[package]] 979 | name = "mio" 980 | version = "0.8.11" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 983 | dependencies = [ 984 | "libc", 985 | "wasi", 986 | "windows-sys 0.48.0", 987 | ] 988 | 989 | [[package]] 990 | name = "nix" 991 | version = "0.23.2" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" 994 | dependencies = [ 995 | "bitflags 1.3.2", 996 | "cc", 997 | "cfg-if", 998 | "libc", 999 | "memoffset 0.6.5", 1000 | ] 1001 | 1002 | [[package]] 1003 | name = "nu-ansi-term" 1004 | version = "0.46.0" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 1007 | dependencies = [ 1008 | "overload", 1009 | "winapi", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "num-traits" 1014 | version = "0.2.18" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 1017 | dependencies = [ 1018 | "autocfg", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "num_cpus" 1023 | version = "1.16.0" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1026 | dependencies = [ 1027 | "hermit-abi", 1028 | "libc", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "object" 1033 | version = "0.32.2" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 1036 | dependencies = [ 1037 | "memchr", 1038 | ] 1039 | 1040 | [[package]] 1041 | name = "once_cell" 1042 | version = "1.19.0" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1045 | 1046 | [[package]] 1047 | name = "ordered-stream" 1048 | version = "0.0.1" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" 1051 | dependencies = [ 1052 | "futures-core", 1053 | "pin-project-lite", 1054 | ] 1055 | 1056 | [[package]] 1057 | name = "overload" 1058 | version = "0.1.1" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1061 | 1062 | [[package]] 1063 | name = "pango" 1064 | version = "0.17.10" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "35be456fc620e61f62dff7ff70fbd54dcbaf0a4b920c0f16de1107c47d921d48" 1067 | dependencies = [ 1068 | "bitflags 1.3.2", 1069 | "gio", 1070 | "glib", 1071 | "libc", 1072 | "once_cell", 1073 | "pango-sys", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "pango-sys" 1078 | version = "0.17.10" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "3da69f9f3850b0d8990d462f8c709561975e95f689c1cdf0fecdebde78b35195" 1081 | dependencies = [ 1082 | "glib-sys", 1083 | "gobject-sys", 1084 | "libc", 1085 | "system-deps", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "parking" 1090 | version = "2.2.0" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" 1093 | 1094 | [[package]] 1095 | name = "parking_lot" 1096 | version = "0.12.2" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" 1099 | dependencies = [ 1100 | "lock_api", 1101 | "parking_lot_core", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "parking_lot_core" 1106 | version = "0.9.10" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1109 | dependencies = [ 1110 | "cfg-if", 1111 | "libc", 1112 | "redox_syscall", 1113 | "smallvec", 1114 | "windows-targets 0.52.5", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "paste" 1119 | version = "1.0.14" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 1122 | 1123 | [[package]] 1124 | name = "pin-project-lite" 1125 | version = "0.2.14" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1128 | 1129 | [[package]] 1130 | name = "pin-utils" 1131 | version = "0.1.0" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1134 | 1135 | [[package]] 1136 | name = "pkg-config" 1137 | version = "0.3.30" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1140 | 1141 | [[package]] 1142 | name = "ppv-lite86" 1143 | version = "0.2.17" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1146 | 1147 | [[package]] 1148 | name = "proc-macro-crate" 1149 | version = "1.3.1" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 1152 | dependencies = [ 1153 | "once_cell", 1154 | "toml_edit 0.19.15", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "proc-macro-error" 1159 | version = "1.0.4" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1162 | dependencies = [ 1163 | "proc-macro-error-attr", 1164 | "proc-macro2", 1165 | "quote", 1166 | "syn 1.0.109", 1167 | "version_check", 1168 | ] 1169 | 1170 | [[package]] 1171 | name = "proc-macro-error-attr" 1172 | version = "1.0.4" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1175 | dependencies = [ 1176 | "proc-macro2", 1177 | "quote", 1178 | "version_check", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "proc-macro2" 1183 | version = "1.0.81" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 1186 | dependencies = [ 1187 | "unicode-ident", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "quote" 1192 | version = "1.0.36" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 1195 | dependencies = [ 1196 | "proc-macro2", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "rand" 1201 | version = "0.8.5" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1204 | dependencies = [ 1205 | "libc", 1206 | "rand_chacha", 1207 | "rand_core", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "rand_chacha" 1212 | version = "0.3.1" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1215 | dependencies = [ 1216 | "ppv-lite86", 1217 | "rand_core", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "rand_core" 1222 | version = "0.6.4" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1225 | dependencies = [ 1226 | "getrandom", 1227 | ] 1228 | 1229 | [[package]] 1230 | name = "redox_syscall" 1231 | version = "0.5.1" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" 1234 | dependencies = [ 1235 | "bitflags 2.5.0", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "redox_users" 1240 | version = "0.4.5" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" 1243 | dependencies = [ 1244 | "getrandom", 1245 | "libredox", 1246 | "thiserror", 1247 | ] 1248 | 1249 | [[package]] 1250 | name = "regex" 1251 | version = "1.10.4" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 1254 | dependencies = [ 1255 | "aho-corasick", 1256 | "memchr", 1257 | "regex-automata", 1258 | "regex-syntax", 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "regex-automata" 1263 | version = "0.4.6" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 1266 | dependencies = [ 1267 | "aho-corasick", 1268 | "memchr", 1269 | "regex-syntax", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "regex-syntax" 1274 | version = "0.8.3" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" 1277 | 1278 | [[package]] 1279 | name = "rustc-demangle" 1280 | version = "0.1.23" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1283 | 1284 | [[package]] 1285 | name = "rustc_version" 1286 | version = "0.4.0" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1289 | dependencies = [ 1290 | "semver", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "rustix" 1295 | version = "0.38.34" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 1298 | dependencies = [ 1299 | "bitflags 2.5.0", 1300 | "errno", 1301 | "libc", 1302 | "linux-raw-sys", 1303 | "windows-sys 0.52.0", 1304 | ] 1305 | 1306 | [[package]] 1307 | name = "rustversion" 1308 | version = "1.0.15" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" 1311 | 1312 | [[package]] 1313 | name = "ryu" 1314 | version = "1.0.17" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 1317 | 1318 | [[package]] 1319 | name = "scopeguard" 1320 | version = "1.2.0" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1323 | 1324 | [[package]] 1325 | name = "semver" 1326 | version = "1.0.22" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 1329 | 1330 | [[package]] 1331 | name = "serde" 1332 | version = "1.0.200" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 1335 | dependencies = [ 1336 | "serde_derive", 1337 | ] 1338 | 1339 | [[package]] 1340 | name = "serde_derive" 1341 | version = "1.0.200" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 1344 | dependencies = [ 1345 | "proc-macro2", 1346 | "quote", 1347 | "syn 2.0.60", 1348 | ] 1349 | 1350 | [[package]] 1351 | name = "serde_json" 1352 | version = "1.0.116" 1353 | source = "registry+https://github.com/rust-lang/crates.io-index" 1354 | checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" 1355 | dependencies = [ 1356 | "itoa", 1357 | "ryu", 1358 | "serde", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "serde_repr" 1363 | version = "0.1.19" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" 1366 | dependencies = [ 1367 | "proc-macro2", 1368 | "quote", 1369 | "syn 2.0.60", 1370 | ] 1371 | 1372 | [[package]] 1373 | name = "serde_spanned" 1374 | version = "0.6.5" 1375 | source = "registry+https://github.com/rust-lang/crates.io-index" 1376 | checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 1377 | dependencies = [ 1378 | "serde", 1379 | ] 1380 | 1381 | [[package]] 1382 | name = "sha1" 1383 | version = "0.6.1" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" 1386 | dependencies = [ 1387 | "sha1_smol", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "sha1_smol" 1392 | version = "1.0.0" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" 1395 | 1396 | [[package]] 1397 | name = "sharded-slab" 1398 | version = "0.1.7" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1401 | dependencies = [ 1402 | "lazy_static", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "signal-hook-registry" 1407 | version = "1.4.2" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1410 | dependencies = [ 1411 | "libc", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "slab" 1416 | version = "0.4.9" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1419 | dependencies = [ 1420 | "autocfg", 1421 | ] 1422 | 1423 | [[package]] 1424 | name = "smallvec" 1425 | version = "1.13.2" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1428 | 1429 | [[package]] 1430 | name = "socket2" 1431 | version = "0.5.7" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1434 | dependencies = [ 1435 | "libc", 1436 | "windows-sys 0.52.0", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "static_assertions" 1441 | version = "1.1.0" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1444 | 1445 | [[package]] 1446 | name = "stray" 1447 | version = "0.1.3" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "358c1637c5ba4ccf1b6a0698de81454db644866cc426d1abc6d357b2efede511" 1450 | dependencies = [ 1451 | "anyhow", 1452 | "byteorder", 1453 | "chrono", 1454 | "log", 1455 | "serde", 1456 | "thiserror", 1457 | "tokio", 1458 | "tokio-stream", 1459 | "tracing", 1460 | "zbus", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "strum" 1465 | version = "0.26.2" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" 1468 | dependencies = [ 1469 | "strum_macros", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "strum_macros" 1474 | version = "0.26.2" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" 1477 | dependencies = [ 1478 | "heck 0.4.1", 1479 | "proc-macro2", 1480 | "quote", 1481 | "rustversion", 1482 | "syn 2.0.60", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "syn" 1487 | version = "1.0.109" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1490 | dependencies = [ 1491 | "proc-macro2", 1492 | "quote", 1493 | "unicode-ident", 1494 | ] 1495 | 1496 | [[package]] 1497 | name = "syn" 1498 | version = "2.0.60" 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" 1500 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 1501 | dependencies = [ 1502 | "proc-macro2", 1503 | "quote", 1504 | "unicode-ident", 1505 | ] 1506 | 1507 | [[package]] 1508 | name = "system-deps" 1509 | version = "6.2.2" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" 1512 | dependencies = [ 1513 | "cfg-expr", 1514 | "heck 0.5.0", 1515 | "pkg-config", 1516 | "toml", 1517 | "version-compare", 1518 | ] 1519 | 1520 | [[package]] 1521 | name = "target-lexicon" 1522 | version = "0.12.14" 1523 | source = "registry+https://github.com/rust-lang/crates.io-index" 1524 | checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" 1525 | 1526 | [[package]] 1527 | name = "tempfile" 1528 | version = "3.10.1" 1529 | source = "registry+https://github.com/rust-lang/crates.io-index" 1530 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 1531 | dependencies = [ 1532 | "cfg-if", 1533 | "fastrand", 1534 | "rustix", 1535 | "windows-sys 0.52.0", 1536 | ] 1537 | 1538 | [[package]] 1539 | name = "thiserror" 1540 | version = "1.0.59" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" 1543 | dependencies = [ 1544 | "thiserror-impl", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "thiserror-impl" 1549 | version = "1.0.59" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" 1552 | dependencies = [ 1553 | "proc-macro2", 1554 | "quote", 1555 | "syn 2.0.60", 1556 | ] 1557 | 1558 | [[package]] 1559 | name = "thread_local" 1560 | version = "1.1.8" 1561 | source = "registry+https://github.com/rust-lang/crates.io-index" 1562 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1563 | dependencies = [ 1564 | "cfg-if", 1565 | "once_cell", 1566 | ] 1567 | 1568 | [[package]] 1569 | name = "tokio" 1570 | version = "1.37.0" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" 1573 | dependencies = [ 1574 | "backtrace", 1575 | "bytes", 1576 | "libc", 1577 | "mio", 1578 | "num_cpus", 1579 | "parking_lot", 1580 | "pin-project-lite", 1581 | "signal-hook-registry", 1582 | "socket2", 1583 | "tokio-macros", 1584 | "windows-sys 0.48.0", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "tokio-macros" 1589 | version = "2.2.0" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 1592 | dependencies = [ 1593 | "proc-macro2", 1594 | "quote", 1595 | "syn 2.0.60", 1596 | ] 1597 | 1598 | [[package]] 1599 | name = "tokio-stream" 1600 | version = "0.1.15" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" 1603 | dependencies = [ 1604 | "futures-core", 1605 | "pin-project-lite", 1606 | "tokio", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "toml" 1611 | version = "0.8.12" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 1614 | dependencies = [ 1615 | "serde", 1616 | "serde_spanned", 1617 | "toml_datetime", 1618 | "toml_edit 0.22.12", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "toml_datetime" 1623 | version = "0.6.5" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 1626 | dependencies = [ 1627 | "serde", 1628 | ] 1629 | 1630 | [[package]] 1631 | name = "toml_edit" 1632 | version = "0.19.15" 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" 1634 | checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" 1635 | dependencies = [ 1636 | "indexmap", 1637 | "toml_datetime", 1638 | "winnow 0.5.40", 1639 | ] 1640 | 1641 | [[package]] 1642 | name = "toml_edit" 1643 | version = "0.22.12" 1644 | source = "registry+https://github.com/rust-lang/crates.io-index" 1645 | checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" 1646 | dependencies = [ 1647 | "indexmap", 1648 | "serde", 1649 | "serde_spanned", 1650 | "toml_datetime", 1651 | "winnow 0.6.7", 1652 | ] 1653 | 1654 | [[package]] 1655 | name = "tracing" 1656 | version = "0.1.40" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1659 | dependencies = [ 1660 | "pin-project-lite", 1661 | "tracing-attributes", 1662 | "tracing-core", 1663 | ] 1664 | 1665 | [[package]] 1666 | name = "tracing-attributes" 1667 | version = "0.1.27" 1668 | source = "registry+https://github.com/rust-lang/crates.io-index" 1669 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1670 | dependencies = [ 1671 | "proc-macro2", 1672 | "quote", 1673 | "syn 2.0.60", 1674 | ] 1675 | 1676 | [[package]] 1677 | name = "tracing-core" 1678 | version = "0.1.32" 1679 | source = "registry+https://github.com/rust-lang/crates.io-index" 1680 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1681 | dependencies = [ 1682 | "once_cell", 1683 | "valuable", 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "tracing-log" 1688 | version = "0.2.0" 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" 1690 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1691 | dependencies = [ 1692 | "log", 1693 | "once_cell", 1694 | "tracing-core", 1695 | ] 1696 | 1697 | [[package]] 1698 | name = "tracing-subscriber" 1699 | version = "0.3.18" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 1702 | dependencies = [ 1703 | "nu-ansi-term", 1704 | "sharded-slab", 1705 | "smallvec", 1706 | "thread_local", 1707 | "tracing-core", 1708 | "tracing-log", 1709 | ] 1710 | 1711 | [[package]] 1712 | name = "uds_windows" 1713 | version = "1.1.0" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" 1716 | dependencies = [ 1717 | "memoffset 0.9.1", 1718 | "tempfile", 1719 | "winapi", 1720 | ] 1721 | 1722 | [[package]] 1723 | name = "unicode-ident" 1724 | version = "1.0.12" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1727 | 1728 | [[package]] 1729 | name = "utf8-width" 1730 | version = "0.1.7" 1731 | source = "registry+https://github.com/rust-lang/crates.io-index" 1732 | checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" 1733 | 1734 | [[package]] 1735 | name = "valuable" 1736 | version = "0.1.0" 1737 | source = "registry+https://github.com/rust-lang/crates.io-index" 1738 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1739 | 1740 | [[package]] 1741 | name = "version-compare" 1742 | version = "0.2.0" 1743 | source = "registry+https://github.com/rust-lang/crates.io-index" 1744 | checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" 1745 | 1746 | [[package]] 1747 | name = "version_check" 1748 | version = "0.9.4" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1751 | 1752 | [[package]] 1753 | name = "wasi" 1754 | version = "0.11.0+wasi-snapshot-preview1" 1755 | source = "registry+https://github.com/rust-lang/crates.io-index" 1756 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1757 | 1758 | [[package]] 1759 | name = "wasm-bindgen" 1760 | version = "0.2.92" 1761 | source = "registry+https://github.com/rust-lang/crates.io-index" 1762 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1763 | dependencies = [ 1764 | "cfg-if", 1765 | "wasm-bindgen-macro", 1766 | ] 1767 | 1768 | [[package]] 1769 | name = "wasm-bindgen-backend" 1770 | version = "0.2.92" 1771 | source = "registry+https://github.com/rust-lang/crates.io-index" 1772 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1773 | dependencies = [ 1774 | "bumpalo", 1775 | "log", 1776 | "once_cell", 1777 | "proc-macro2", 1778 | "quote", 1779 | "syn 2.0.60", 1780 | "wasm-bindgen-shared", 1781 | ] 1782 | 1783 | [[package]] 1784 | name = "wasm-bindgen-macro" 1785 | version = "0.2.92" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1788 | dependencies = [ 1789 | "quote", 1790 | "wasm-bindgen-macro-support", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "wasm-bindgen-macro-support" 1795 | version = "0.2.92" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1798 | dependencies = [ 1799 | "proc-macro2", 1800 | "quote", 1801 | "syn 2.0.60", 1802 | "wasm-bindgen-backend", 1803 | "wasm-bindgen-shared", 1804 | ] 1805 | 1806 | [[package]] 1807 | name = "wasm-bindgen-shared" 1808 | version = "0.2.92" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1811 | 1812 | [[package]] 1813 | name = "winapi" 1814 | version = "0.3.9" 1815 | source = "registry+https://github.com/rust-lang/crates.io-index" 1816 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1817 | dependencies = [ 1818 | "winapi-i686-pc-windows-gnu", 1819 | "winapi-x86_64-pc-windows-gnu", 1820 | ] 1821 | 1822 | [[package]] 1823 | name = "winapi-i686-pc-windows-gnu" 1824 | version = "0.4.0" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1827 | 1828 | [[package]] 1829 | name = "winapi-x86_64-pc-windows-gnu" 1830 | version = "0.4.0" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1833 | 1834 | [[package]] 1835 | name = "windows-core" 1836 | version = "0.52.0" 1837 | source = "registry+https://github.com/rust-lang/crates.io-index" 1838 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1839 | dependencies = [ 1840 | "windows-targets 0.52.5", 1841 | ] 1842 | 1843 | [[package]] 1844 | name = "windows-sys" 1845 | version = "0.48.0" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1848 | dependencies = [ 1849 | "windows-targets 0.48.5", 1850 | ] 1851 | 1852 | [[package]] 1853 | name = "windows-sys" 1854 | version = "0.52.0" 1855 | source = "registry+https://github.com/rust-lang/crates.io-index" 1856 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1857 | dependencies = [ 1858 | "windows-targets 0.52.5", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "windows-targets" 1863 | version = "0.48.5" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1866 | dependencies = [ 1867 | "windows_aarch64_gnullvm 0.48.5", 1868 | "windows_aarch64_msvc 0.48.5", 1869 | "windows_i686_gnu 0.48.5", 1870 | "windows_i686_msvc 0.48.5", 1871 | "windows_x86_64_gnu 0.48.5", 1872 | "windows_x86_64_gnullvm 0.48.5", 1873 | "windows_x86_64_msvc 0.48.5", 1874 | ] 1875 | 1876 | [[package]] 1877 | name = "windows-targets" 1878 | version = "0.52.5" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 1881 | dependencies = [ 1882 | "windows_aarch64_gnullvm 0.52.5", 1883 | "windows_aarch64_msvc 0.52.5", 1884 | "windows_i686_gnu 0.52.5", 1885 | "windows_i686_gnullvm", 1886 | "windows_i686_msvc 0.52.5", 1887 | "windows_x86_64_gnu 0.52.5", 1888 | "windows_x86_64_gnullvm 0.52.5", 1889 | "windows_x86_64_msvc 0.52.5", 1890 | ] 1891 | 1892 | [[package]] 1893 | name = "windows_aarch64_gnullvm" 1894 | version = "0.48.5" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1897 | 1898 | [[package]] 1899 | name = "windows_aarch64_gnullvm" 1900 | version = "0.52.5" 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" 1902 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 1903 | 1904 | [[package]] 1905 | name = "windows_aarch64_msvc" 1906 | version = "0.48.5" 1907 | source = "registry+https://github.com/rust-lang/crates.io-index" 1908 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1909 | 1910 | [[package]] 1911 | name = "windows_aarch64_msvc" 1912 | version = "0.52.5" 1913 | source = "registry+https://github.com/rust-lang/crates.io-index" 1914 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 1915 | 1916 | [[package]] 1917 | name = "windows_i686_gnu" 1918 | version = "0.48.5" 1919 | source = "registry+https://github.com/rust-lang/crates.io-index" 1920 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1921 | 1922 | [[package]] 1923 | name = "windows_i686_gnu" 1924 | version = "0.52.5" 1925 | source = "registry+https://github.com/rust-lang/crates.io-index" 1926 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 1927 | 1928 | [[package]] 1929 | name = "windows_i686_gnullvm" 1930 | version = "0.52.5" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 1933 | 1934 | [[package]] 1935 | name = "windows_i686_msvc" 1936 | version = "0.48.5" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1939 | 1940 | [[package]] 1941 | name = "windows_i686_msvc" 1942 | version = "0.52.5" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 1945 | 1946 | [[package]] 1947 | name = "windows_x86_64_gnu" 1948 | version = "0.48.5" 1949 | source = "registry+https://github.com/rust-lang/crates.io-index" 1950 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1951 | 1952 | [[package]] 1953 | name = "windows_x86_64_gnu" 1954 | version = "0.52.5" 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" 1956 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 1957 | 1958 | [[package]] 1959 | name = "windows_x86_64_gnullvm" 1960 | version = "0.48.5" 1961 | source = "registry+https://github.com/rust-lang/crates.io-index" 1962 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1963 | 1964 | [[package]] 1965 | name = "windows_x86_64_gnullvm" 1966 | version = "0.52.5" 1967 | source = "registry+https://github.com/rust-lang/crates.io-index" 1968 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 1969 | 1970 | [[package]] 1971 | name = "windows_x86_64_msvc" 1972 | version = "0.48.5" 1973 | source = "registry+https://github.com/rust-lang/crates.io-index" 1974 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1975 | 1976 | [[package]] 1977 | name = "windows_x86_64_msvc" 1978 | version = "0.52.5" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 1981 | 1982 | [[package]] 1983 | name = "winnow" 1984 | version = "0.5.40" 1985 | source = "registry+https://github.com/rust-lang/crates.io-index" 1986 | checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" 1987 | dependencies = [ 1988 | "memchr", 1989 | ] 1990 | 1991 | [[package]] 1992 | name = "winnow" 1993 | version = "0.6.7" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" 1996 | dependencies = [ 1997 | "memchr", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "zbus" 2002 | version = "2.3.2" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "2d8f1a037b2c4a67d9654dc7bdfa8ff2e80555bbefdd3c1833c1d1b27c963a6b" 2005 | dependencies = [ 2006 | "async-broadcast", 2007 | "async-channel", 2008 | "async-executor", 2009 | "async-lock", 2010 | "async-recursion", 2011 | "async-task", 2012 | "async-trait", 2013 | "byteorder", 2014 | "derivative", 2015 | "dirs", 2016 | "enumflags2", 2017 | "event-listener", 2018 | "futures-core", 2019 | "futures-sink", 2020 | "futures-util", 2021 | "hex", 2022 | "lazy_static", 2023 | "nix", 2024 | "once_cell", 2025 | "ordered-stream", 2026 | "rand", 2027 | "serde", 2028 | "serde_repr", 2029 | "sha1", 2030 | "static_assertions", 2031 | "tokio", 2032 | "tracing", 2033 | "uds_windows", 2034 | "winapi", 2035 | "zbus_macros", 2036 | "zbus_names", 2037 | "zvariant", 2038 | ] 2039 | 2040 | [[package]] 2041 | name = "zbus_macros" 2042 | version = "2.3.2" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "1f8fb5186d1c87ae88cf234974c240671238b4a679158ad3b94ec465237349a6" 2045 | dependencies = [ 2046 | "proc-macro-crate", 2047 | "proc-macro2", 2048 | "quote", 2049 | "regex", 2050 | "syn 1.0.109", 2051 | ] 2052 | 2053 | [[package]] 2054 | name = "zbus_names" 2055 | version = "2.6.1" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" 2058 | dependencies = [ 2059 | "serde", 2060 | "static_assertions", 2061 | "zvariant", 2062 | ] 2063 | 2064 | [[package]] 2065 | name = "zvariant" 2066 | version = "3.15.2" 2067 | source = "registry+https://github.com/rust-lang/crates.io-index" 2068 | checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" 2069 | dependencies = [ 2070 | "byteorder", 2071 | "enumflags2", 2072 | "libc", 2073 | "serde", 2074 | "static_assertions", 2075 | "zvariant_derive", 2076 | ] 2077 | 2078 | [[package]] 2079 | name = "zvariant_derive" 2080 | version = "3.15.2" 2081 | source = "registry+https://github.com/rust-lang/crates.io-index" 2082 | checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" 2083 | dependencies = [ 2084 | "proc-macro-crate", 2085 | "proc-macro2", 2086 | "quote", 2087 | "syn 1.0.109", 2088 | "zvariant_utils", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "zvariant_utils" 2093 | version = "1.0.1" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" 2096 | dependencies = [ 2097 | "proc-macro2", 2098 | "quote", 2099 | "syn 1.0.109", 2100 | ] 2101 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hybrid-bar" 3 | description = "A simple status bar made for wlroots compositors." 4 | authors = [ "varsity " ] 5 | readme = "README.md" 6 | repository = "https://github.com/vars1ty/HybridBar" 7 | keywords = [ "hyprland", "wayland", "status-bar", "gtk-layer-shell", "linux" ] 8 | categories = [ "rendering", "gui" ] 9 | version = "0.4.9" 10 | edition = "2021" 11 | license = "MIT" 12 | 13 | [dependencies] 14 | tokio = { version = "1.26.0", features = [ "full" ] } 15 | tracing-subscriber = "0.3.16" 16 | gtk-layer-shell = "0.6.0" 17 | lazy_static = "1.4.0" 18 | hyprland = "0.3.0" 19 | lxinfo = "0.1.2" 20 | stray = "0.1.3" 21 | glib = "0.17.2" 22 | json = "0.12.4" 23 | gtk = "0.17.0" 24 | 25 | [profile.release] 26 | codegen-units = 1 27 | panic = "abort" 28 | opt-level = "z" 29 | strip = true 30 | lto = "fat" 31 | -------------------------------------------------------------------------------- /DUAL-CONFIG.md: -------------------------------------------------------------------------------- 1 | # Dual-Configuration 2 | If you want to make for say, a basic static taskbar at the bottom whilst still preserving your top-positioned config and stylesheet, you are able to do just that. 3 | 4 | ## How? 5 | First, make a secondary configuration-file that you want to use for your bottom-bar, name it whatever. If you want to use separate stylesheets, you may make a new one as well. 6 | 7 | In order to tell Hybrid **what** stylesheet to use for *(x)* configuration, put the full name of the stylesheet inside `hybrid` -> `stylesheet`, for example: `"stylesheet": "bottom.css"`. 8 | 9 | To actually launch Hybrid and pin it to the bottom with a separate configuration-file, follow these simple steps: 10 | 1. Open the config you want to use 11 | 2. At `hybrid`, set the `position` keys value to `Bottom`, non-case sensitive 12 | 3. Save the config and then run `HYBRID_CONFIG=(name_of_config.json) hybrid-bar` and done! 13 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | - Can I hot-reload my changes? 3 | - No, there's no built-in support for this. 4 | 5 | - Are PRs accepted? 6 | - Yeah, as long as they follow the existing code structure and don't introduce breaking changes without a really good reason as to why it's needed. 7 | 8 | - Can I create custom widget types? 9 | - No you can't create full-on custom types. 10 | 11 | - How do I move widgets up and down? 12 | - You can't move them up/down through the actual config, although you can somewhat do it via CSS. 13 | 14 | - Can I split the bar into sections, like with Waybar and/or eww? 15 | - **Available since 0.3.4** 16 | - Yes you can specify where the bar should be located at. 17 | - Use `hybrid` -> `expand_left` / `expand_right` (`bool`) and mess around with them until you find what you like. 18 | - Default values are `true` for both; a.k.a expanded across your monitor. 19 | - > **Note**: Aligning your bar to one side only may result in weird bugs, such as it not allocating a region of free space below it. 20 | 21 | - Does this only act as a status bar? 22 | - It's intended purpose is to be a status bar, but you can turn it into a smaller application shortcut launcher through scripts and buttons. 23 | 24 | - Hybrid crashed for me 25 | - Send in a bug report with the logs and your configuration (JSON + CSS). 26 | 27 | - Why is markup only supported on static labels? 28 | - Because if you for say, focus a window with some special characters and it's being drawn in markup, it'll freak out. I might be able to fix it later on, but for now it's not happening. 29 | 30 | - I want to add widgets at runtime 31 | - Not possible for the time being. 32 | 33 | - I'm tired of repeating long commands in the JSON, can variables be added? 34 | - **Available since 0.2.9** 35 | - Read `VARIABLES.md`. 36 | 37 | - Does Hybrid work outside Sway and Hyprland? 38 | - Yes, it's been proven to work on KDE and will most definitely work on GNOME. 39 | 40 | - Does Hybrid work on X11? 41 | - Which part of **Wayland status bar** did you not understand? 42 | 43 | - The config is too hard for me to understand 44 | - Then you either lack a functional brain, or you are the definition of lazy. 45 | 46 | - Can I make the bar not take up my entire top/bottom part of the screen? 47 | - To an extent yeah, read `Can I split the bar into sections, like with Waybar and/or eww?`. 48 | 49 | - Is there support for distros other than Arch? 50 | - Hybrid should work on every distro, only difference being that I won't officially ship it to anything but the AUR Repo and on GitHub Releases. 51 | - If you however decide to package it and want to ship it yourself, go ahead and submit an issue which holds the URL to the package, if it's git or not, etc. 52 | 53 | - I want to move Hybrid to a different monitor 54 | - **Available since 0.3.2** 55 | - You can specify your monitor via `hybrid` -> `monitor` (`i32`) 56 | 57 | - Can I change the namespace for blurring Hybrid Bar on Hyprland? 58 | - **Available since 0.3.9** 59 | - Yes you can thanks to this PR: https://github.com/vars1ty/HybridBar/pull/27, option: `hybrid` -> `namespace` (`String`) 60 | 61 | - How do I enable optional features/experiments? 62 | - **Available since 0.4.9** 63 | - Create a key named `features` in `hybrid`, then specify what features you want. For example: 64 | - `"features": ["tray_experimental", "hyprland]` 65 | -------------------------------------------------------------------------------- /FEATURES.md: -------------------------------------------------------------------------------- 1 | # Features 2 | In 0.4.9 and higher, you may enable additional features in Hybrid Bar, including experimental ones. 3 | 4 | The non-experimental features listed are usually disabled for a variety of reasons: 5 | - Compatibility concerns 6 | - It may start background tasks which the user might not want 7 | - The feature is slow and is generally not recommended using 8 | 9 | ## Standard Features 10 | 11 | 1. System Information via lxinfo (`systemd`) 12 | - Allows for calling aliases such as `%username%` which utilize libc calls over commands, which are typically faster 13 | - This is disabled by default because it relies on files such as `/etc/os-release` to be present on your system. 14 | 2. Hyprland Support (`hyprland`) 15 | - Adds the `%hl_workspace%` and `%hl_window%` aliases. 16 | - Workspace gets the current workspace id, whereas Window gets the focused window title. 17 | 18 | ## Experimental Features 19 | > **Warning**: Experimental features are under development. They may break, change or be removed entirely. 20 | 21 | 1. System Tray (`tray_experimental`) 22 | - Introduced in version `0.4.4`, marked as experimental in version `0.4.6` 23 | - New widget: `tray` - using the `stray` crate 24 | - Not likely to become available as a stable feature anytime soon, because `stray` is overall hacky and a mess. 25 | ```json 26 | { 27 | "left-tray_tray": {} 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 varsity 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PRESETS.md: -------------------------------------------------------------------------------- 1 | # Presets 2 | A list of others Hybrid configs. 3 | 4 | - [Mac OS inspired status bar](https://github.com/alexstr-dev/hybrid-macbar) by alexstr-dev and me 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hybrid Bar 2 | A simple status bar focused on wlroots Wayland compositors 3 | 4 | ## ⚠️ Project Ended 5 | Hybrid Bar har been ended as the codebase is too messy and lack of interest on maintaining it. 6 | 7 | You are free to fork the project and continue it on your own, if you want a potential future alternative: [Crisp](https://github.com/vars1ty/Crisp). 8 | 9 | ## Preview 10 | With blur through Hyprland. 11 | ![image](https://user-images.githubusercontent.com/54314240/197680577-2bc0cff4-2438-4c8d-8428-11499d0519c6.png) 12 | 13 | The bottom bar is also made with Hybrid. 14 | 15 | ## What does it support? 16 | It supports: 17 | - Straight-forward configuration; 18 | - Labels with shell commands (+ listen support); 19 | - Spacings (a.k.a. Separators if styled); 20 | - Boxes with child widgets; 21 | - Custom update-rate for dynamic labels; 22 | - Cava; 23 | - Buttons with shell commands; 24 | - Tooltips for buttons and labels; 25 | - Markup for buttons and labels; 26 | - Top and Bottom pinning; 27 | - Transparency (+ blur if your compositor allows for blurring surface layers); 28 | - Experimental system tray via `stray`; 29 | - Different monitors for each configuration 30 | 31 | ## I have no config 32 | If you installed outside the AUR, copy the example from `examples/config.json` into `~/.config/HybridBar/`. 33 | 34 | ## Does it only work on wlroots Compositors? 35 | Nope, it's been tested on KDE as well and it worked just fine. 36 | 37 | It just won't work on GNOME as it hasn't implemented the `wlr-layer-shell` protocol. 38 | 39 | # Config Layout 40 | I'm assuming you are familiar with JSON. If you aren't, well too bad. 41 | 42 | ## Base Setup 43 | Before you can use the bar, you have to adjust the color and alpha. 44 | 45 | RGB Colors are 0-255 as a 32-bit integer, Alpha is 0.0-1.0 as a 32-bit float. 46 | 47 | Here's an example: 48 | 49 | ```json 50 | { 51 | "hybrid": { 52 | "namespace": "hybrid-bar", 53 | "r": 10, 54 | "g": 10, 55 | "b": 10, 56 | "a": 0.5 57 | } 58 | } 59 | ``` 60 | ## CSS Support 61 | CSS is supported and you can make it auto-load on startup by making a `style.css` file next to your `config.json` on the same path. 62 | 63 | If you want a sample CSS which has good defaults, check `examples/style.css`. 64 | 65 | ## Environment Variables 66 | `HYBRID_LOG` = `1` : Logs Hybrid output to stdout. 67 | 68 | `HYBRID_CONFIG` = `name.json` : Locates the config inside the HybridBar config path, then uses it for the rest of the bars session. 69 | 70 | ## Background Updates 71 | Hybrid automatically performs background updates for dynamic labels/tooltips and Cava widgets. 72 | 73 | The rate for updating labels is read from the labels `update_rate` key (u64). 74 | 75 | It's worth noting that low update-rates may lead to performance decreases, the value specified is in **milliseconds**. 76 | 77 | # Build dependencies 78 | 1. rust 79 | 2. gtk-layer-shell 80 | 3. gtk3 81 | 4. a compositor that supports layer-shells 82 | - This excludes GNOME. KDE, Hyprland and Sway have been confirmed working. 83 | 84 | ## Installation 85 | Arch Linux: `yay/paru -S hybrid-bar` 86 | 87 | Other distros: `cargo install hybrid-bar` 88 | 89 | ## Building 90 | 1. `git clone https://github.com/vars1ty/HybridBar` 91 | 2. `cd HybridBar` 92 | 3. `cargo build --release` 93 | 4. `cd target/release` 94 | 5. Done, the executable is called `hybrid-bar`. 95 | -------------------------------------------------------------------------------- /VARIABLES.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | This is only supported on Hybrid version 0.2.9 and higher. 3 | *** 4 | 5 | ## How do I make variables? 6 | You can make variables by specifying them in a similar format as `hybrid`, here's an example: 7 | 8 | ```json 9 | "variables": { 10 | "cool_message": "a very cool message indeed" 11 | } 12 | ``` 13 | 14 | All variables have to be a child-key of `variables`, otherwise Hybrid won't be able to find it. 15 | 16 | ## But.. How do I use my epic variable? 17 | To actually use your variable, you can do something like this: 18 | 19 | ```json 20 | "left-label_mvp": { 21 | "text": "cool_message" 22 | } 23 | ``` 24 | 25 | Then the text will automatically be replaced with the value from `cool_message`. Variables are supported on these keys: 26 | - `text` 27 | - `command` 28 | - `tooltip` 29 | -------------------------------------------------------------------------------- /WIDGETS.md: -------------------------------------------------------------------------------- 1 | # Widgets 2 | > **Note** 3 | > If you add widgets that aren't attached to a `box` widget, you have to specify how the widget should be placed. 4 | > 5 | > For example: `left-label_UNIQUE_NAME` 6 | > 7 | > For child widgets inside of boxes, you can simply define the widget as `label_UNIQUE_NAME`. 8 | 9 | Available widgets: 10 | 11 | `label`: 12 | 13 | Keys Supported: 14 | - text: String 15 | - Constant Text string 16 | - command: String 17 | - Command to be executed and appended to the back of the text string 18 | - update_rate: u64 19 | - How often the command should be called if `listen` isn't enabled 20 | - tooltip: String 21 | - Constant Tooltip string 22 | - tooltip_command: String 23 | - Command to be executed and appended to the back of the tooltip string 24 | - listen: bool 25 | - Should Hybrid listen to the commands output, rather than calling it once and moving on? 26 | - update_anim: String 27 | - **Dynamic Labels only**: What animation should be played when the content has been updated? 28 | - Values: `none`, `crossfade` (Default), `slide_left` and `slide_right` 29 | - anim_duration: u32 30 | - **Dynamic Labels only**: How long (in milliseconds) should the update animation last? Default is `250`. 31 | *** 32 | `button`: 33 | 34 | Keys Supported: 35 | - text: String 36 | - Constant Text string 37 | - command: String 38 | - Command to be executed when pressing on the button 39 | - tooltip: String 40 | - Constant Tooltip string 41 | - tooltip_command: String 42 | - Command to be executed and appended to the back of the tooltip string 43 | *** 44 | `spacing`: 45 | 46 | Keys Supported: 47 | - spacing_start: i32 48 | - Start spacing 49 | - spacing_end: i32 50 | - End spacing 51 | *** 52 | `box`: 53 | 54 | Keys Supported: 55 | - width: i32 56 | - Box custom requested width 57 | - widgets: JSON 58 | - Embedded child widgets 59 | *** 60 | `cava`: 61 | 62 | Keys Supported: 63 | - Shared: `hybrid` -> `cava_update_rate`: u64 64 | - How often the new Cava output should be displayed, in milliseconds 65 | - Shared: `hybrid` -> `cava_sed`: String 66 | - SED to be used for translating the raw stdout into custom content 67 | - Shared: `hybrid` -> `cava_bars`: i32 68 | - How many internal Cava bars that should be outputted 69 | - Shared: `hybrid` -> `cava_framerate`: i32 70 | - Internal Cava framerate 71 | *** 72 | `tray`: 73 | 74 | > **Warning**: Experimental Widget, expect issues. 75 | 76 | Keys Supported: 77 | - None 78 | *** 79 | To actually use a widget, here's an example: 80 | 81 | ```json 82 | "left-label_UNIQUE_NAME": { 83 | "text": "whomai stdout ", 84 | "command": "whoami", 85 | "?": "The update-rate below is set to 1 second in milliseconds.", 86 | "update_rate": 1000 87 | } 88 | ``` 89 | 90 | Every widget **has** to contain an underscore (`_`) after the type, then you add the unique name for that widget. 91 | 92 | If you don't specify a name for the widget after the underscore, the bar will crash and say that discarded names aren't supported. 93 | 94 | **NOTE**: Widgets with the same name (regardless of type) aren't officially supported and may suffer from weird behavior. 95 | 96 | The `text` and `command` nested JSON keys are simply described as: 97 | - text: Raw Label Text 98 | - command: Optional bash command 99 | 100 | **All** keys are optional, if you skip `text` for example, it'll be using an empty value. 101 | 102 | No, the unique name isn't actually displayed anywhere, it's just to be able to differ each component from another. 103 | 104 | ## Cava 105 | Here's an example of how you may setup Cava: `"right-cava_0": {}`. 106 | 107 | You may also change how all Cava widgets are displayed on the bar through these keys in `hybrid`: 108 | - `cava_sed`: String - The sed for Cava. If left empty, `s/;//g;s/0/▁/g;s/1/▂/g;s/2/▃/g;s/3/▄/g;s/4/▅/g;s/5/▆/g;s/6/▇/g;s/7/█/g;` will be used; 109 | - `cava_framerate`: u32 (min 60, max 360) - How fast Cava should check for audio levels and output it to `stdout` for Hybrid to then sync to the viewport; 110 | - `cava_bars`: u32 (min 2, max 16) - How many bars that should be rendered for each Cava widget; 111 | - `cava_update_rate`: u64 (min 1, default 1) - How often (in milliseconds) Hybrid should check for back-end Cava updates, parse and then display it. 112 | 113 | ### Performance 114 | Because the implementation isn't perfect and relies on listening to a raw Cava stdout, the performance may fluctuate. 115 | 116 | On mid/high-end systems this should really not even be noticeable, going from `~0.0%` CPU-Usage without Cava, to `~0.4%` with Cava. 117 | 118 | If you don't want the very small performance impact, simply don't use Cava. Or if your bar is already active and you want to disable Cava; `killall -I cava -9` - This will kill Cava and disable its functionality from Hybrid until you restart the bar. 119 | 120 | ### What if Cava crashes unexpectedly? 121 | If Cava crashes (or closes) unexpectedly then Hybrid will effectively cut off the module entirely and all of its update-loops, allowing the session to keep on running. 122 | -------------------------------------------------------------------------------- /examples/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "hybrid": { 3 | "namespace": "hybrid-bar", 4 | "r": 10, 5 | "g": 10, 6 | "b": 10, 7 | "a": 1.0 8 | }, 9 | "left-label_username": { 10 | "text": "user: ", 11 | "command": "whoami", 12 | "tooltip": "And here's your kernel: ", 13 | "tooltip_command": "uname -r", 14 | "update_rate": 50000 15 | }, 16 | "centered-label_centerdummy": { 17 | "text": "Example Config - Create/Edit yours at ~/.config/HybridBar/config.json", 18 | "tooltip": "An example of a centered label" 19 | }, 20 | "right-label_rightdummy": { 21 | "text": "dummy right aligned label", 22 | "tooltip": "An example of a right-aligned label" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/style.css: -------------------------------------------------------------------------------- 1 | /* Label Color. Config default is white. */ 2 | label { 3 | color: white; 4 | } 5 | 6 | /* Button Configuration */ 7 | button { 8 | font-weight: normal; 9 | border: none; 10 | box-shadow: none; 11 | margin-bottom: -8px; 12 | margin-top: -8px; 13 | padding-right: 0; 14 | padding-left: 0; 15 | } 16 | -------------------------------------------------------------------------------- /resources/cava_tmp.conf: -------------------------------------------------------------------------------- 1 | # Cava Configuration for Hybrid 2 | [general] 3 | framerate = [framerate] 4 | bars = [bars] 5 | [output] 6 | method = raw 7 | raw_target = /dev/stdout 8 | data_format = ascii 9 | ascii_max_range = 7 10 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::{constants::*, structures::ConfigData, utils::environment}; 2 | use json::JsonValue; 3 | use std::{ 4 | collections::HashMap, 5 | fs, 6 | sync::{RwLock, RwLockReadGuard}, 7 | }; 8 | 9 | lazy_static! { 10 | /// Cached config. 11 | pub static ref CONFIG: RwLock = RwLock::new(read_config_raw()); 12 | } 13 | 14 | /// Gets the root home path to Hybrid. 15 | pub fn get_path() -> String { 16 | format!( 17 | "{}/.config/HybridBar/", 18 | std::env::var("HOME").unwrap_or_else(|_| execute!("whoami")) 19 | ) 20 | } 21 | 22 | /// Returns the set update-rate. 23 | pub fn get_update_rate() -> u64 { 24 | let update_rate = conf!(HYBRID_ROOT_JSON, "update_rate", false, false) 25 | .number 26 | .unwrap_or_else(|| 100) 27 | .clamp(5, 10_000); 28 | 29 | update_rate.try_into().expect(ERR_PARSE_UPDATE_RATE) 30 | } 31 | 32 | // Parses and returns the config. 33 | fn read_config_raw() -> JsonValue { 34 | let mut conf_path = get_path(); 35 | conf_path.push_str(&environment::try_get_var("HYBRID_CONFIG", DEFAULT_CONFIG)); 36 | json::parse( 37 | &fs::read_to_string(&conf_path) 38 | // Don't panic if the file doesn't exist/couldn't be read. Instead use the example config. 39 | .unwrap_or_else(|_| include_str!("../examples/config.json").to_owned()), 40 | ) 41 | .unwrap_or_else(|error| panic!("[ERROR] Error parsing config: {error}")) 42 | } 43 | 44 | /// Tries to fetch a value from the config. Supported types are `String` and `i32`. 45 | pub fn try_get(root: &str, key: &str, is_string: bool, with_custom_variables: bool) -> ConfigData { 46 | let cfg = &get_config()[root]; 47 | if cfg.has_key(key) { 48 | let grabbed_value = &cfg[key]; 49 | 50 | // If the desired value isn't a string, try and get it as a 32-bit integer. 51 | if !is_string { 52 | return ConfigData::new( 53 | None, 54 | Some( 55 | grabbed_value 56 | .as_i32() 57 | .unwrap_or_else(|| panic!("[ERROR] Failed parsing {root}:{key} as i32!")), 58 | ), 59 | ); 60 | } 61 | 62 | // Convert it to a string-value. 63 | if with_custom_variables { 64 | ConfigData::new( 65 | Some(with_variables( 66 | grabbed_value.to_string(), 67 | &get_custom_variables(), 68 | )), 69 | None, 70 | ) 71 | } else { 72 | ConfigData::new(Some(grabbed_value.to_string()), None) 73 | } 74 | } else { 75 | // The key wasn't found, so just return None on all values. 76 | ConfigData::default() 77 | } 78 | } 79 | 80 | /// Returns the entire config. 81 | pub fn get_config<'a>() -> RwLockReadGuard<'a, JsonValue> { 82 | CONFIG.read().expect(ERR_ACCESS_CONFIG) 83 | } 84 | 85 | /// Gets all the custom variables. 86 | pub fn get_custom_variables() -> HashMap { 87 | let cfg = &get_config()[HYBRID_V_ROOT_JSON]; 88 | let mut map: HashMap = HashMap::new(); 89 | for entry in cfg.entries() { 90 | map.insert(entry.0.to_owned(), entry.1.to_string()); 91 | } 92 | 93 | map 94 | } 95 | 96 | /// Replaces any variable-matching patterns in the `String` with the variables value. 97 | pub fn with_variables(input: String, custom_variables: &HashMap) -> String { 98 | let mut input = input; 99 | for variable in custom_variables { 100 | // Only replace if `result` actually contains the defined variable. 101 | if input.contains(variable.0) { 102 | input = input.replace(variable.0, variable.1); 103 | } 104 | } 105 | 106 | input 107 | } 108 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | // This file is meant for holding constant strings / repeatedly-used numerical values. 2 | 3 | /// Master Hybrid JSON Key. 4 | pub const HYBRID_ROOT_JSON: &str = "hybrid"; 5 | /// Master Variables JSON Key. 6 | pub const HYBRID_V_ROOT_JSON: &str = "variables"; 7 | /// Master Features JSON Key. 8 | pub const HYBRID_F_ROOT_JSON: &str = "features"; 9 | /// Process to be used for calling external commands. 10 | pub const PROC_TARGET: &str = "sh"; 11 | /// Default stylesheet file. 12 | pub const DEFAULT_CSS: &str = "style.css"; 13 | /// Default config file. 14 | pub const DEFAULT_CONFIG: &str = "config.json"; 15 | /// Cava temporary config path. 16 | pub const CAVA_TMP_CONFIG: &str = "/tmp/cava_tmp_hybrid.conf"; 17 | 18 | // Constant errors. 19 | 20 | pub const ERR_PARSE_CAVA_UPDATE_RATE: &str = 21 | "[ERROR] hybrid:cava_update_rate couldn't be parsed into a 64-bit unsigned integer (u64)!"; 22 | pub const ERR_PARSE_UPDATE_RATE: &str = "[ERROR] Cannot convert update_rate into u64!"; 23 | pub const ERR_ACCESS_CAVA_INSTANCES: &str = "[ERROR] Couldn't access ui::CAVA_INSTANCES!"; 24 | pub const ERR_INVALID_POS: &str = 25 | "[ERROR] Invalid position! Values: [ TOP, BOTTOM ] - casing doesn't matter."; 26 | pub const ERR_GET_DISPLAY: &str = 27 | "[ERROR] Couldn't find a valid display, is your compositor doing alright?"; 28 | pub const ERR_GET_MONITOR: &str = "[ERROR] Couldn't find a valid monitor."; 29 | pub const ERR_SCREEN_DEFAULT: &str = "[ERROR] Couldn't find a valid screen!"; 30 | pub const ERR_LOAD_SAMPLE_CSS: &str = "[ERROR] Failed loading the example stylesheet!"; 31 | pub const ERR_CUSTOM_DRAW: &str = 32 | "[ERROR] Failed drawing Hybrid using custom color sources, which is needed for transparency!"; 33 | pub const ERR_INVALID_WIDGET_FORMAT: &str = 34 | "[ERROR] Widgets should be named as [alignment]-[widget_type]_[name]"; 35 | pub const ERR_EMPTY_NAME: &str = 36 | "[ERROR] Found an empty widget name, this is not currently supported!"; 37 | pub const ERR_INVALID_ALIGNMENT: &str = 38 | "[ERROR] Invalid widget alignment! Valid values are: [ left, centered, right ]"; 39 | pub const ERR_TAKE_STDOUT: &str = "[ERROR] Cannot take stdout from child process!"; 40 | pub const ERR_NO_LINES: &str = "[ERROR] There are no more lines available!"; 41 | pub const ERR_STRING_NONE: &str = "[ERROR] The string value is None!"; 42 | pub const ERR_NO_LXINFO: &str = 43 | "System Info isn't available for this system, therefore aliases have been disabled."; 44 | pub const ERR_WRITE_TMP_CONF: &str = "[ERROR] Failed writing to the temporary Cava config!"; 45 | pub const ERR_START_CAVA: &str = "[ERROR] Cannot start Cava script!"; 46 | pub const ERR_CREATE_RT: &str = "[ERROR] Couldn't create the Tokio runtime!"; 47 | pub const ERR_SEND_MSG_UI: &str = "[ERROR] Couldn't send tray messages to the UI!"; 48 | pub const ERR_ACCESS_CONFIG: &str = "[ERROR] Couldn't access CONFIG!"; 49 | pub const ERR_WRONG_LABEL_RANIM: &str = 50 | "[ERROR] Invalid revealer animation! Use `crossfade`, `slide_left` or `slide_right`."; 51 | 52 | // Constant warnings. 53 | 54 | pub const WARN_CAVA_NO_LINES: &str = "[WARN] Cava: There are no more lines available. Hybrid will keep on running but Cava will be stopped!"; 55 | pub const WARN_CAVA_NO_BARS_INSTANCE: &str = "[WARN] Cava: Failed accessing cava::BARS, stopping!"; 56 | pub const WARN_CAVA_NO_CRASHED_INSTANCE: &str = 57 | "[WARN] Cava: Failed accessing cava::HAS_CAVA_CRASHED, stopping!"; 58 | -------------------------------------------------------------------------------- /src/loop.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::{ 3 | ERR_ACCESS_CAVA_INSTANCES, ERR_PARSE_CAVA_UPDATE_RATE, HYBRID_ROOT_JSON, 4 | WARN_CAVA_NO_BARS_INSTANCE, WARN_CAVA_NO_CRASHED_INSTANCE, 5 | }, 6 | utils::cava::{self, HAS_CAVA_CRASHED}, 7 | widget::HWidget, 8 | }; 9 | use glib::Continue; 10 | use std::time::Duration; 11 | 12 | /// Updates dynamic bar content. 13 | pub fn update() { 14 | // Only start the tick-loop if there are actually Cava widgets available. 15 | let widgets = cava::CAVA_INSTANCES 16 | .lock() 17 | .expect(ERR_ACCESS_CAVA_INSTANCES); 18 | if widgets.is_empty() { 19 | return; 20 | } 21 | 22 | // Run the `update_cava` closure every x ms. 23 | glib::timeout_add_local( 24 | Duration::from_millis( 25 | conf!(HYBRID_ROOT_JSON, "cava_update_rate", false, false) 26 | .number 27 | .unwrap_or_else(|| 1) 28 | .try_into() 29 | .expect(ERR_PARSE_CAVA_UPDATE_RATE), 30 | ), 31 | update_cava, 32 | ); 33 | } 34 | 35 | /// Updates all Cava widgets. 36 | fn update_cava() -> Continue { 37 | if let Ok(ref bars) = cava::BARS.lock() { 38 | // Loop through all Cava widget instances and sync the text. 39 | let widgets = cava::CAVA_INSTANCES 40 | .lock() 41 | .expect(ERR_ACCESS_CAVA_INSTANCES); 42 | let widgets = widgets.iter(); 43 | for widget in widgets { 44 | widget.update_label_direct(bars); 45 | } 46 | 47 | if let Ok(has_cava_crashed) = HAS_CAVA_CRASHED.lock() { 48 | glib::Continue(!*has_cava_crashed) 49 | } else { 50 | log!(WARN_CAVA_NO_CRASHED_INSTANCE); 51 | glib::Continue(false) 52 | } 53 | } else { 54 | log!(WARN_CAVA_NO_BARS_INSTANCE); 55 | glib::Continue(false) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | /// Logs a [HYBRID] [DEBUG] formatted message to stdout. 3 | macro_rules! log { 4 | ($msg:expr) => { 5 | if $crate::utils::environment::try_get_var("HYBRID_LOG", "0") == "1" { 6 | println!("[LOG]: {}", $msg) 7 | } 8 | }; 9 | } 10 | #[macro_export] 11 | /// Executes a bash command and outputs it to `result`. 12 | macro_rules! execute { 13 | ($cmd:expr) => {{ 14 | let mut result = unsafe { 15 | String::from_utf8_unchecked( 16 | std::process::Command::new($crate::constants::PROC_TARGET) 17 | .args(["-c", $cmd]) 18 | .output() 19 | .unwrap() 20 | .stdout, 21 | ) 22 | }; 23 | 24 | // Remove the last character as its a new line. 25 | result.pop(); 26 | 27 | result 28 | }}; 29 | } 30 | 31 | #[macro_export] 32 | /// Gets a value from the config. 33 | macro_rules! conf { 34 | ($root:expr, $key:expr, $is_string:expr, $with_custom_variables:expr) => { 35 | $crate::config::try_get($root, $key, $is_string, $with_custom_variables) 36 | }; 37 | ($root:expr, $key:expr, $default:expr) => { 38 | if let Some(res) = conf!($root, $key, true, false).string { 39 | res == "true" 40 | } else { 41 | $default 42 | } 43 | }; 44 | } 45 | 46 | #[macro_export] 47 | /// Checks if the specified feature is active. 48 | macro_rules! is_feature_active { 49 | ($tag:literal) => { 50 | $crate::config::get_config()[$crate::constants::HYBRID_ROOT_JSON] 51 | [$crate::constants::HYBRID_F_ROOT_JSON] 52 | .contains($tag) 53 | }; 54 | } 55 | 56 | #[macro_export] 57 | /// Restarts the given `Revealer` and plays the given animation after the `after` closure has 58 | /// finished. 59 | macro_rules! restart_revealer { 60 | ($revealer:expr, $after:expr, $anim:expr, $speed:expr) => { 61 | if $anim == RevealerTransitionType::None { 62 | // No transition, skip full restart and instead just call directly. 63 | $after(); 64 | } else { 65 | $revealer.set_transition_duration(0); 66 | $revealer.set_reveal_child(false); 67 | $revealer.set_transition_type(RevealerTransitionType::None); 68 | $after(); 69 | $revealer.set_transition_duration($speed); 70 | $revealer.set_transition_type($anim); 71 | $revealer.set_reveal_child(true); 72 | } 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | #[macro_use] 4 | extern crate lazy_static; 5 | 6 | #[macro_use] 7 | mod macros; 8 | 9 | mod config; 10 | mod constants; 11 | mod r#loop; 12 | mod structures; 13 | mod ui; 14 | mod utils; 15 | mod widget; 16 | mod widgets; 17 | 18 | use constants::*; 19 | use gtk::gdk::*; 20 | use gtk::gio::ApplicationFlags; 21 | use gtk::prelude::*; 22 | use gtk::*; 23 | use gtk_layer_shell::Edge; 24 | use json::JsonValue; 25 | use std::path::Path; 26 | 27 | use crate::utils::hyprland; 28 | 29 | /// Gets the anchors. 30 | fn get_anchors() -> [(gtk_layer_shell::Edge, bool); 4] { 31 | let expand_left = conf!(HYBRID_ROOT_JSON, "expand_left", true); 32 | let expand_right = conf!(HYBRID_ROOT_JSON, "expand_right", true); 33 | 34 | let pos = conf!(HYBRID_ROOT_JSON, "position", true, false) 35 | .string 36 | .unwrap_or_else(|| "Top".to_owned()); 37 | 38 | if !pos.eq_ignore_ascii_case("Top") && !pos.eq_ignore_ascii_case("Bottom") && !pos.is_empty() { 39 | panic!("{}", ERR_INVALID_POS) 40 | } 41 | 42 | // If the position was valid, return the result. 43 | [ 44 | (Edge::Left, expand_left), 45 | (Edge::Right, expand_right), 46 | (Edge::Top, pos.eq_ignore_ascii_case("Top") || pos.is_empty()), 47 | (Edge::Bottom, pos.eq_ignore_ascii_case("Bottom")), 48 | ] 49 | } 50 | 51 | /// Initializes the status bar. 52 | fn activate(application: &Application) { 53 | // Create a normal GTK window however you like 54 | let window = ApplicationWindow::new(application); 55 | window.connect_screen_changed(set_visual); 56 | window.connect_draw(draw); 57 | 58 | // Initialize layer shell before the window has been fully initialized. 59 | gtk_layer_shell::init_for_window(&window); 60 | 61 | // Order above normal windows 62 | // Prior to 0.2.9, this was set to Bottom but it caused issues with tooltips being shown below 63 | // windows. 64 | gtk_layer_shell::set_layer(&window, gtk_layer_shell::Layer::Top); 65 | 66 | // Push other windows out of the way 67 | // Toggling this off may help some if they are in applications that have weird unicode text, which may mess with the bars scaling. 68 | gtk_layer_shell::auto_exclusive_zone_enable(&window); 69 | 70 | for (anchor, state) in get_anchors() { 71 | gtk_layer_shell::set_anchor(&window, anchor, state); 72 | } 73 | 74 | // Allows for specifing the namespace of the layer. 75 | // The default is "gtk-layer-shell" to not break existing configs. 76 | let namespace = conf!(HYBRID_ROOT_JSON, "namespace", true, false) 77 | .string 78 | .unwrap_or_else(|| "gtk-layer-shell".to_owned()); 79 | 80 | gtk_layer_shell::set_namespace(&window, &namespace); 81 | 82 | // Initialize gdk::Display by default value, which is decided by the compositor. 83 | let display = Display::default().expect(ERR_GET_DISPLAY); 84 | 85 | // Loads the monitor variable from config, default is 0. 86 | let config_monitor = conf!(HYBRID_ROOT_JSON, "monitor", false, false) 87 | .number 88 | .unwrap_or_default(); 89 | 90 | // Gets the actual gdk::Monitor from configured number. 91 | let monitor = display.monitor(config_monitor).expect(ERR_GET_MONITOR); 92 | 93 | // Sets which monitor should be used for the bar. 94 | gtk_layer_shell::set_monitor(&window, &monitor); 95 | 96 | // For transparency to work. 97 | window.set_app_paintable(true); 98 | 99 | // Build all the widgets. 100 | ui::build_widgets(&window); 101 | log!("Ready!"); 102 | } 103 | 104 | /// Loads the CSS 105 | pub fn load_css() { 106 | let provider = CssProvider::new(); 107 | // 0.2.8: Allow for defining the name of the stylesheet to look up 108 | let css_file = conf!(HYBRID_ROOT_JSON, "stylesheet", true, false) 109 | .string 110 | .unwrap_or_else(|| DEFAULT_CSS.to_owned()); 111 | 112 | let mut css_path = config::get_path(); 113 | css_path.push_str(&css_file); 114 | 115 | if Path::new(&css_path).is_file() { 116 | provider 117 | .load_from_path(&css_path) 118 | .unwrap_or_else(|error| panic!("[ERROR] Error loading stylesheet: {error}")) 119 | } else { 120 | provider 121 | .load_from_data(include_bytes!("../examples/style.css")) 122 | .expect(ERR_LOAD_SAMPLE_CSS); 123 | log!("No custom stylesheet was found, using ../examples/style.css") 124 | } 125 | 126 | // Add the provider to the default screen 127 | StyleContext::add_provider_for_screen( 128 | &Screen::default().expect(ERR_SCREEN_DEFAULT), 129 | &provider, 130 | STYLE_PROVIDER_PRIORITY_APPLICATION, 131 | ); 132 | } 133 | 134 | /// Called upon application startup. 135 | #[no_mangle] 136 | #[tokio::main] 137 | async fn main() { 138 | log!("Building application..."); 139 | let application = Application::new(None, ApplicationFlags::default()); 140 | log!("Loading CSS..."); 141 | application.connect_startup(|_| load_css()); 142 | log!("Creating viewport..."); 143 | // Activate the layer shell. 144 | application.connect_activate(|app| { 145 | activate(app); 146 | }); 147 | 148 | if is_feature_active!("tray_experimental") { 149 | tracing_subscriber::fmt::init(); 150 | } 151 | 152 | application.run(); 153 | } 154 | 155 | /// Applies custom visuals. 156 | fn set_visual(window: &ApplicationWindow, screen: Option<&Screen>) { 157 | if let Some(screen) = screen { 158 | if let Some(ref visual) = screen.rgba_visual() { 159 | window.set_visual(Some(visual)); // Needed for transparency, not available in GTK 4+ so 160 | // F. 161 | } 162 | } 163 | } 164 | 165 | /// Converts the value of a child inside `background` to a `f64`. 166 | fn get_background_float(cfg: &JsonValue, identifier: &str, from_255: bool) -> f64 { 167 | let mut res = cfg[HYBRID_ROOT_JSON][identifier] 168 | .as_f64() 169 | .unwrap_or_else(|| panic!("[ERROR] Failed converting hybrid:{identifier} to f64!")); 170 | 171 | // Only divide by 255 if explicitly told to. 172 | if from_255 { 173 | res /= 255.0; 174 | } 175 | 176 | // Return the result. 177 | res.clamp(0.0, 255.0) 178 | } 179 | 180 | /// Draws the window using a custom color and opacity. 181 | fn draw(_: &ApplicationWindow, ctx: &cairo::Context) -> Inhibit { 182 | let cfg = config::CONFIG.read().unwrap(); 183 | 184 | // Fetch config for the values. 185 | let r = get_background_float(&cfg, "r", true); 186 | let g = get_background_float(&cfg, "g", true); 187 | let b = get_background_float(&cfg, "b", true); 188 | let a = get_background_float(&cfg, "a", false); 189 | 190 | // Apply 191 | ctx.set_source_rgba(r, g, b, a); 192 | ctx.set_operator(cairo::Operator::Screen); 193 | ctx.paint().expect(ERR_CUSTOM_DRAW); 194 | Inhibit(false) 195 | } 196 | -------------------------------------------------------------------------------- /src/structures.rs: -------------------------------------------------------------------------------- 1 | use gtk::RevealerTransitionType; 2 | 3 | use crate::widget::Align; 4 | 5 | /// Fetched config data. 6 | #[derive(Default)] 7 | pub struct ConfigData { 8 | pub string: Option, 9 | pub number: Option, 10 | } 11 | 12 | /// Implements `new` for Config Data. 13 | impl ConfigData { 14 | /// Creates a new Config Data instance and returns it. 15 | pub fn new(string: Option, number: Option) -> ConfigData { 16 | ConfigData { string, number } 17 | } 18 | } 19 | 20 | /// Base keys. 21 | pub struct BaseKeys { 22 | pub text: String, 23 | pub command: String, 24 | pub update_rate: u64, 25 | pub tooltip: String, 26 | pub tooltip_command: String, 27 | pub alignment: Align, 28 | } 29 | 30 | pub trait RevealerExtensions { 31 | fn from_str(string: &str) -> Option; 32 | } 33 | 34 | impl RevealerExtensions for RevealerTransitionType { 35 | /// Tries to get the transition type based on the string input. 36 | /// Note: The string has to be lowercase and spaces replaced with underscores. 37 | /// This can only return `Crossfade`, `SlideLeft` and `SlideRight`. 38 | fn from_str(string: &str) -> Option { 39 | match string { 40 | "crossfade" => Some(RevealerTransitionType::Crossfade), 41 | "slide_left" => Some(RevealerTransitionType::SlideLeft), 42 | "slide_right" => Some(RevealerTransitionType::SlideRight), 43 | _ => None 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ui.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | config::{get_custom_variables, with_variables, CONFIG}, 3 | r#loop::update, 4 | structures::{BaseKeys, RevealerExtensions}, 5 | utils::cava::{self, HAS_CAVA_STARTED}, 6 | *, 7 | }; 8 | use crate::{ 9 | widget::{Align, HWidget}, 10 | widgets::{ 11 | box_widget::BoxWidget, button_widget::ButtonWidget, cava_widget::CavaWidget, 12 | label_widget::LabelWidget, spacing_widget::SpacingWidget, tray_widget::TrayWidget, 13 | }, 14 | }; 15 | use gtk::traits::*; 16 | 17 | /// Adds and aligns the specified widget. 18 | pub fn add_and_align( 19 | widget: &impl IsA, 20 | align: Align, 21 | left: &Box, 22 | centered: &Box, 23 | right: &Box, 24 | box_holder: Option<&Box>, 25 | ) { 26 | if let Some(r#box) = box_holder { 27 | r#box.add(widget) 28 | } else { 29 | match align { 30 | Align::Left => left.add(widget), 31 | Align::Centered => centered.add(widget), 32 | Align::Right => right.add(widget), 33 | } 34 | } 35 | } 36 | 37 | /// Builds all of the widgets. 38 | pub fn build_widgets(window: &ApplicationWindow) { 39 | // Create box widgets, which we'll be using to draw the content onto. 40 | let root = Box::new(Orientation::Horizontal, 0); 41 | let left = Box::new(Orientation::Horizontal, 0); 42 | let centered = Box::new(Orientation::Horizontal, 0); 43 | let right = Box::new(Orientation::Horizontal, 0); 44 | 45 | // 0.2.5: Root expands across the entire bar, previously "left" would do this but it isn't 46 | // ideal when customizing, since borders would draw on the entire bar rather than just on the 47 | // left portion of the bar. 48 | root.set_widget_name("root"); 49 | 50 | // 0.2.5: Allow for customizing left, centered and right. 51 | left.set_widget_name("left"); 52 | centered.set_widget_name("centered"); 53 | right.set_widget_name("right"); 54 | 55 | root.set_center_widget(Some(¢ered)); 56 | root.pack_end(&right, false, true, 0); 57 | root.add(&left); 58 | window.add(&root); 59 | 60 | // Prepare and show all of the widgets. 61 | create_components(&left, ¢ered, &right); 62 | window.show_all(); 63 | 64 | // Update dynamic content. 65 | update(); 66 | } 67 | 68 | /// Gets the base key values. 69 | pub fn get_base_keys(root: &JsonValue) -> (String, String, u64, String, String) { 70 | let custom_variables = &get_custom_variables(); 71 | let text = with_variables( 72 | root["text"].as_str().unwrap_or_default().to_owned(), 73 | custom_variables, 74 | ); 75 | let command = with_variables( 76 | root["command"].as_str().unwrap_or_default().to_owned(), 77 | custom_variables, 78 | ); 79 | let update_rate: u64 = root["update_rate"] 80 | .as_i32() 81 | .unwrap_or(100) 82 | .try_into() 83 | .unwrap_or_else(|_| panic!("[ERROR] Couldn't convert update_rate to u64! Source: {root}")); 84 | let tooltip = with_variables( 85 | root["tooltip"].as_str().unwrap_or_default().to_owned(), 86 | custom_variables, 87 | ); 88 | let tooltip_command = with_variables( 89 | root["tooltip_command"] 90 | .as_str() 91 | .unwrap_or_default() 92 | .to_owned(), 93 | custom_variables, 94 | ); 95 | (text, command, update_rate, tooltip, tooltip_command) 96 | } 97 | 98 | /// Creates all of the widgets. 99 | fn create_components(left: &Box, centered: &Box, right: &Box) { 100 | // Add all of the widgets defined from the config. 101 | if let Ok(cfg) = CONFIG.read() { 102 | const ALIGNMENT: char = '-'; 103 | const SEPARATOR: &str = "_"; 104 | let relevant = cfg 105 | .entries() 106 | .filter(|(key, _)| key.contains(ALIGNMENT) && key.contains(SEPARATOR)); 107 | 108 | for (key, json) in relevant { 109 | // Gets the widget identifiers. 110 | let identifiers: Vec<_> = key.split(SEPARATOR).collect(); 111 | 112 | // Identifier example: `left-label_ABC` <= `left-label` is the IDENTIFIER, `ABC` is the NAME. 113 | let identifier = identifiers[0]; 114 | 115 | // Grabs widget alignment and widget type from the identifier separated by '-'. 116 | let (widget_alignment, widget_type) = identifier 117 | .split_once(ALIGNMENT) 118 | .expect(ERR_INVALID_WIDGET_FORMAT); 119 | 120 | // Base keys, all being optional. 121 | let (text, command, update_rate, tooltip, tooltip_command) = get_base_keys(json); 122 | let base_keys = BaseKeys { 123 | text, 124 | command, 125 | update_rate, 126 | tooltip, 127 | tooltip_command, 128 | alignment: Align::from_str(widget_alignment).expect(ERR_INVALID_ALIGNMENT), 129 | }; 130 | 131 | // Gets every element after the widget identifier, then appends '_' in between. 132 | let widget_name = identifiers[1..].join(SEPARATOR); 133 | 134 | if widget_name.is_empty() { 135 | panic!("{}", ERR_EMPTY_NAME) 136 | } 137 | 138 | log!(format!( 139 | "Adding widget '{identifier}' with alignment '{widget_alignment}'!", 140 | )); 141 | 142 | // Add the widget. 143 | add_widget( 144 | json, 145 | (widget_type, &widget_name), 146 | base_keys, 147 | (left, centered, right), 148 | identifier, 149 | None, 150 | ) 151 | } 152 | } else { 153 | panic!("{}", ERR_ACCESS_CONFIG) 154 | } 155 | } 156 | 157 | /// Add a new widget of specified identifier. 158 | pub fn add_widget( 159 | key: &JsonValue, 160 | widget_pkg: (&str, &str), 161 | base_keys: BaseKeys, 162 | left_centered_right: (&Box, &Box, &Box), 163 | identifier: &str, 164 | box_holder: Option<&Box>, 165 | ) { 166 | // Extract name and type. 167 | let (widget_type, widget_name) = widget_pkg; 168 | 169 | // Extract data from the base keys. 170 | let text = base_keys.text; 171 | let command = base_keys.command; 172 | let update_rate = base_keys.update_rate; 173 | let tooltip = base_keys.tooltip; 174 | let tooltip_command = base_keys.tooltip_command; 175 | let alignment = base_keys.alignment; 176 | 177 | // Extract left, centered and right. 178 | let (left, centered, right) = left_centered_right; 179 | 180 | match widget_type { 181 | "label" => { 182 | let label = LabelWidget { 183 | tooltip, 184 | tooltip_command, 185 | text, 186 | command, 187 | update_rate, 188 | label: Label::new(None), 189 | listen: key["listen"].as_bool().unwrap_or_default(), 190 | revealer: Revealer::new(), 191 | update_anim: RevealerTransitionType::from_str( 192 | key["update_anim"].as_str().unwrap_or("crossfade"), 193 | ), 194 | anim_duration: key["anim_duration"].as_u32().unwrap_or(250), 195 | }; 196 | 197 | label.add(widget_name, alignment, left, centered, right, box_holder) 198 | } 199 | "button" => { 200 | let button = ButtonWidget { 201 | tooltip, 202 | tooltip_command, 203 | command, 204 | button: Button::with_label(&text), 205 | }; 206 | 207 | button.add(widget_name, alignment, left, centered, right, box_holder) 208 | } 209 | "spacing" => { 210 | let spacing = SpacingWidget { 211 | spacing_start: key["spacing_start"].as_i32().unwrap_or_default(), 212 | spacing_end: key["spacing_end"].as_i32().unwrap_or_default(), 213 | }; 214 | 215 | spacing.add(widget_name, alignment, left, centered, right, box_holder) 216 | } 217 | "box" => { 218 | let box_widget = BoxWidget { 219 | width: key["width"].as_i32().unwrap_or_default(), 220 | widgets: key["widgets"].to_owned(), 221 | }; 222 | 223 | box_widget.add(widget_name, alignment, left, centered, right, box_holder) 224 | } 225 | "cava" => { 226 | let cava = CavaWidget { 227 | label: Label::new(None), 228 | }; 229 | 230 | if let Ok(mut has_cava_started) = HAS_CAVA_STARTED.lock() { 231 | if !*has_cava_started { 232 | cava::update_bars(); 233 | // Ensure it only calls update_bars once. 234 | *has_cava_started = true 235 | } 236 | } 237 | 238 | cava.add(widget_name, alignment, left, centered, right, box_holder) 239 | } 240 | "tray" => TrayWidget.add(widget_name, alignment, left, centered, right, box_holder), 241 | _ => { 242 | panic!("[ERROR] There is no widget type defined as '{identifier}'!\n") 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/utils/aliases.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::ERR_NO_LXINFO; 2 | use crate::hyprland; 3 | use lxinfo::info; 4 | 5 | /// Replaces `find` with `replace` if found. 6 | fn replace_if_present(content: &mut String, find: &str, replace: &str) { 7 | if content.contains(find) { 8 | *content = content.replace(find, replace); 9 | } 10 | } 11 | 12 | /// Checks if the first and last character in `content` is `%`. 13 | fn has_alias_chars(content: &str) -> bool { 14 | if content.len() <= 4 { 15 | return false; 16 | } 17 | 18 | let mut chars = content.chars(); 19 | chars.next().unwrap() == '%' && chars.last().unwrap() == '%' 20 | } 21 | 22 | /// Checks if the `content` contains any of the built-in aliases, then replaces it with the real 23 | /// value. 24 | pub fn use_aliases(content: &str) -> String { 25 | if !has_alias_chars(content) { 26 | // Not an alias, execute and return. 27 | return execute!(content); 28 | } 29 | 30 | let mut content = content.to_owned(); 31 | if is_feature_active!("hyprland") { 32 | let data = hyprland::get_data(); 33 | replace_if_present(&mut content, "%hl_workspace%", &data.workspace.to_string()); 34 | replace_if_present(&mut content, "%hl_window%", &data.window); 35 | if !has_alias_chars(&content) { 36 | // Success 37 | return content; 38 | } 39 | } 40 | 41 | if is_feature_active!("systemd") { 42 | if let Some(info) = info::get_system_information() { 43 | replace_if_present(&mut content, "%username%", &info.username); 44 | replace_if_present(&mut content, "%hostname%", &info.hostname); 45 | replace_if_present(&mut content, "%shell%", &info.shell); 46 | replace_if_present(&mut content, "%kernel%", &info.kernel); 47 | replace_if_present(&mut content, "%used_mem%", &info.used_mem); 48 | replace_if_present(&mut content, "%distro_id%", &info.distro_id); 49 | replace_if_present(&mut content, "%total_mem%", &info.total_mem); 50 | replace_if_present(&mut content, "%cached_mem%", &info.cached_mem); 51 | replace_if_present(&mut content, "%available_mem%", &info.available_mem); 52 | replace_if_present(&mut content, "%distro%", &info.distro_name); 53 | replace_if_present(&mut content, "%distro_build_id%", &info.distro_build_id); 54 | if !has_alias_chars(&content) { 55 | // Success 56 | return content; 57 | } 58 | } else { 59 | panic!("{}", ERR_NO_LXINFO); 60 | } 61 | } 62 | 63 | execute!(&content) 64 | } 65 | -------------------------------------------------------------------------------- /src/utils/cava.rs: -------------------------------------------------------------------------------- 1 | use crate::{constants::*, widgets::cava_widget::CavaWidget}; 2 | use std::{fs::write, process::Stdio, sync::Mutex}; 3 | use tokio::{ 4 | io::{AsyncBufReadExt, BufReader}, 5 | process::Command, 6 | task, 7 | }; 8 | 9 | lazy_static! { 10 | /// Has Cava been started yet? 11 | pub static ref HAS_CAVA_STARTED: Mutex = Mutex::new(false); 12 | /// Current Cava bars. 13 | pub static ref BARS: Mutex = Mutex::new(String::default()); 14 | /// Has Cava crashed? If true, don't keep `update_cava` running. 15 | pub static ref HAS_CAVA_CRASHED: Mutex = Mutex::new(false); 16 | /// All active Cava widget instances. 17 | pub static ref CAVA_INSTANCES: Mutex> = Mutex::new(Vec::new()); 18 | } 19 | 20 | /// Gets the sed to use for Cava. 21 | pub fn get_sed() -> String { 22 | conf!(HYBRID_ROOT_JSON, "cava_sed", true, false) 23 | .string 24 | .unwrap_or_else(|| { 25 | "s/;//g;s/0/▁/g;s/1/▂/g;s/2/▃/g;s/3/▄/g;s/4/▅/g;s/5/▆/g;s/6/▇/g;s/7/█/g;".to_owned() 26 | }) 27 | } 28 | 29 | /// Returns the amount of bars that should be present. 30 | fn get_bars() -> i32 { 31 | let bars = conf!(HYBRID_ROOT_JSON, "cava_bars", false, false) 32 | .number 33 | .unwrap_or_else(|| 5); 34 | bars.clamp(2, 16) 35 | } 36 | 37 | /// Returns the desired framerate to use for Cava updates. 38 | fn get_framerate() -> i32 { 39 | let framerate = conf!(HYBRID_ROOT_JSON, "cava_framerate", false, false) 40 | .number 41 | .unwrap_or_else(|| 60); 42 | framerate.clamp(60, 360) 43 | } 44 | 45 | /// Builds the temporary Cava configuration and then returns the path to it, 46 | pub fn get_temp_config() -> String { 47 | let path = CAVA_TMP_CONFIG.to_owned(); 48 | // 0.2.7: Support for dynamically configuring the temporary config to an extent. 49 | let bars = get_bars(); 50 | let framerate = get_framerate(); 51 | let mut conf = include_str!("../../resources/cava_tmp.conf"); 52 | let formatted = conf 53 | .replace("[framerate]", &framerate.to_string()) 54 | .replace("[bars]", &bars.to_string()); 55 | 56 | conf = &formatted; 57 | write(&path, conf).expect(ERR_WRITE_TMP_CONF); 58 | path 59 | } 60 | 61 | /// Updates the `BARS` value with Cava. 62 | /// Only call this once as it's a loop. 63 | pub fn update_bars() { 64 | task::spawn(async move { 65 | let mut bars; 66 | let sed = get_sed(); 67 | let path = get_temp_config(); 68 | let mut child = Command::new(PROC_TARGET) 69 | .args(["-c", &format!("cava -p {path} | sed -u '{sed}'")]) 70 | .stdout(Stdio::piped()) 71 | .kill_on_drop(true) 72 | .spawn() 73 | .expect(ERR_START_CAVA); 74 | 75 | let out = child.stdout.take().expect(ERR_TAKE_STDOUT); 76 | 77 | // Drop to free the resources as we don't need to access them anymore. 78 | drop(path); 79 | drop(sed); 80 | let mut reader = BufReader::new(out).lines(); 81 | loop { 82 | bars = reader 83 | .next_line() 84 | .await 85 | .unwrap_or_else(|_| on_cava_crashed()) 86 | .unwrap_or_else(|| on_cava_crashed()); 87 | 88 | if let Ok(mut r_bars) = BARS.lock() { 89 | *r_bars = bars; 90 | } 91 | } 92 | }); 93 | } 94 | 95 | /// Called when Cava has crashed. 96 | fn on_cava_crashed() -> ! { 97 | *HAS_CAVA_CRASHED.lock().unwrap() = true; 98 | BARS.lock().unwrap().clear(); 99 | panic!("{}", WARN_CAVA_NO_LINES) 100 | } 101 | -------------------------------------------------------------------------------- /src/utils/environment.rs: -------------------------------------------------------------------------------- 1 | /// Tries to get the value from a specific environment variable. 2 | pub fn try_get_var(variable: &str, fallback_value: &str) -> String { 3 | std::env::var(variable).unwrap_or_else(|_| fallback_value.to_owned()) 4 | } 5 | -------------------------------------------------------------------------------- /src/utils/hyprland.rs: -------------------------------------------------------------------------------- 1 | use hyprland::{ 2 | data::{Client, Workspace}, 3 | shared::{HyprDataActive, HyprDataActiveOptional}, 4 | }; 5 | 6 | /// Hyprland data. 7 | pub struct HyprlandData { 8 | pub workspace: i32, 9 | pub window: String, 10 | } 11 | 12 | /// Gets the workspace and active window. 13 | pub fn get_data() -> HyprlandData { 14 | HyprlandData { 15 | workspace: Workspace::get_active().unwrap().id, 16 | window: get_active_window_title(), 17 | } 18 | } 19 | 20 | /// Gets and returns the active window title 21 | fn get_active_window_title() -> String { 22 | match Client::get_active().unwrap() { 23 | Some(window) => window.title, 24 | None => String::default(), 25 | } 26 | } -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod aliases; 2 | pub mod cava; 3 | pub mod environment; 4 | pub mod hyprland; 5 | -------------------------------------------------------------------------------- /src/widget.rs: -------------------------------------------------------------------------------- 1 | use gtk::Box; 2 | 3 | /// Widget alignment. 4 | #[derive(Clone, Copy)] 5 | pub enum Align { 6 | Left, 7 | Centered, 8 | Right, 9 | } 10 | 11 | /// Implements `from_str` for the Align structure. 12 | impl Align { 13 | /// Tries to get the enum by its string-identifier. 14 | /// Note: Only lowercase letters will be detected. 15 | pub fn from_str(string: &str) -> Option { 16 | match string { 17 | "left" => Some(Align::Left), 18 | "centered" => Some(Align::Centered), 19 | "right" => Some(Align::Right), 20 | _ => None, 21 | } 22 | } 23 | } 24 | 25 | /// Implements basic traits for custom user-defined widgets. 26 | pub trait HWidget { 27 | /// Invoked when the widget should be added. 28 | fn add( 29 | self, 30 | name: &str, 31 | align: Align, 32 | left: &Box, 33 | centered: &Box, 34 | right: &Box, 35 | box_holder: Option<&Box>, 36 | ); 37 | 38 | /// Label Widget: Tells the label to update content to the specified new content. 39 | #[allow(unused_variables)] 40 | fn update_label_direct(&self, new_content: &str) {} 41 | 42 | /// Label Widget: Tells the label to update with custom-defined behavior, for example from a local buffer. 43 | fn update_label_internal(&self) {} 44 | 45 | /// Function dedicated to starting optional loops. 46 | fn start_loop(&mut self) {} 47 | } 48 | -------------------------------------------------------------------------------- /src/widgets/box_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::ERR_EMPTY_NAME, 3 | structures::BaseKeys, 4 | ui, 5 | widget::{Align, HWidget}, 6 | }; 7 | use gtk::{traits::*, *}; 8 | use json::JsonValue; 9 | 10 | /// Creates a new basic box widget. 11 | pub struct BoxWidget { 12 | pub width: i32, 13 | pub widgets: JsonValue, 14 | } 15 | 16 | /// Builds the child widgets. 17 | fn build_child_widgets( 18 | widgets: JsonValue, 19 | left: &Box, 20 | centered: &Box, 21 | right: &Box, 22 | box_holder: &Box, 23 | ) { 24 | const SEPARATOR: &str = "_"; 25 | let relevant = widgets.entries().filter(|(key, _)| key.contains(SEPARATOR)); 26 | 27 | for (key, json) in relevant { 28 | // Gets the widget identifiers. 29 | let identifiers: Vec<_> = key.split(SEPARATOR).collect(); 30 | 31 | // Type example: `label_ABC` <= `label` is the IDENTIFIER, `ABC` is the NAME. 32 | let widget_type = identifiers[0]; 33 | 34 | // Base keys. 35 | let (text, command, update_rate, tooltip, tooltip_command) = ui::get_base_keys(json); 36 | let base_keys = BaseKeys { 37 | text, 38 | command, 39 | update_rate, 40 | tooltip, 41 | tooltip_command, 42 | alignment: Align::Left, // <= Doesn't matter as it won't be used. 43 | }; 44 | 45 | let widget_name = identifiers[1..].join(SEPARATOR); 46 | if widget_name.is_empty() { 47 | panic!("{}", ERR_EMPTY_NAME) 48 | } 49 | 50 | log!(format!( 51 | "Adding child widget '{widget_name}', type '{widget_type}' into '{}'!", 52 | box_holder.widget_name() 53 | )); 54 | 55 | // Add the widget. 56 | ui::add_widget( 57 | json, 58 | (widget_type, &widget_name), 59 | base_keys, 60 | (left, centered, right), 61 | widget_type, 62 | Some(box_holder), 63 | ) 64 | } 65 | } 66 | 67 | // Implements HWidget for the widget so that we can actually use it. 68 | impl HWidget for BoxWidget { 69 | fn add( 70 | self, 71 | name: &str, 72 | align: Align, 73 | left: &Box, 74 | centered: &Box, 75 | right: &Box, 76 | box_holder: Option<&Box>, 77 | ) { 78 | let widget = Box::new(Orientation::Horizontal, 0); 79 | widget.set_widget_name(name); 80 | widget.set_width_request(self.width); 81 | 82 | // 0.4.3: Experimental: Allow for widgets enclosed into boxes. 83 | // 0.4.7: Stabilize Box Child-Widgets. 84 | if !self.widgets.is_null() { 85 | build_child_widgets(self.widgets, left, centered, right, &widget) 86 | } 87 | 88 | ui::add_and_align(&widget, align, left, centered, right, box_holder); 89 | log!("Added a new box widget"); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/widgets/button_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ui, 3 | utils::aliases::use_aliases, 4 | widget::{Align, HWidget}, 5 | }; 6 | use gtk::{glib::GString, traits::*, *}; 7 | use std::{mem::take, time::Duration}; 8 | 9 | /// Creates a new button widget. 10 | pub struct ButtonWidget { 11 | pub tooltip: String, 12 | pub tooltip_command: String, 13 | pub command: String, 14 | pub button: Button, 15 | } 16 | 17 | // Implements HWidget for the widget so that we can actually use it. 18 | impl HWidget for ButtonWidget { 19 | fn add( 20 | mut self, 21 | name: &str, 22 | align: Align, 23 | left: &Box, 24 | centered: &Box, 25 | right: &Box, 26 | box_holder: Option<&Box>, 27 | ) { 28 | self.button.set_widget_name(name); 29 | // 0.2.8: Support tooltips for buttons 30 | self.button.set_tooltip_markup(Some(&self.tooltip)); 31 | 32 | // 0.3.6: Support for commands on tooltips. 33 | if !self.tooltip_command.is_empty() { 34 | self.start_loop(); 35 | } 36 | 37 | // If the command isn't empty, subscribe to click events. 38 | if !self.command.is_empty() { 39 | self.button.connect_clicked(move |_| { 40 | execute!(&self.command); 41 | }); 42 | } 43 | 44 | ui::add_and_align(&self.button, align, left, centered, right, box_holder); 45 | log!("Added a new button widget"); 46 | } 47 | 48 | fn start_loop(&mut self) { 49 | let button = self.button.clone(); 50 | let tooltip = take(&mut self.tooltip); 51 | let tooltip_command = take(&mut self.tooltip_command); 52 | let tick = move || { 53 | let mut new_tooltip = String::default(); 54 | new_tooltip.push_str(&tooltip); 55 | new_tooltip.push_str(&use_aliases(&tooltip_command)); 56 | 57 | let tooltip_markup = button.tooltip_markup().unwrap_or_else(|| GString::from("")); 58 | if !tooltip_markup.eq(&new_tooltip) { 59 | // Markup support here, the user therefore has to deal with any upcoming issues due to 60 | // the command output, on their own. 61 | button.set_tooltip_markup(Some(&new_tooltip)); 62 | } 63 | 64 | glib::Continue(true) 65 | }; 66 | 67 | tick(); 68 | // NOTE: This does NOT respect update_rate, since it's not meant to update super fast. 69 | glib::timeout_add_local(Duration::from_millis(1000), tick); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/widgets/cava_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::ERR_ACCESS_CAVA_INSTANCES, 3 | ui, 4 | utils::cava, 5 | widget::{Align, HWidget}, 6 | }; 7 | use gtk::{traits::*, *}; 8 | 9 | /// Creates a new label widget. 10 | #[derive(Debug)] 11 | pub struct CavaWidget { 12 | pub label: Label, 13 | } 14 | 15 | unsafe impl Send for CavaWidget {} 16 | unsafe impl Sync for CavaWidget {} 17 | 18 | // Implements HWidget for the widget so that we can actually use it. 19 | impl HWidget for CavaWidget { 20 | fn add( 21 | self, 22 | name: &str, 23 | align: Align, 24 | left: &Box, 25 | centered: &Box, 26 | right: &Box, 27 | box_holder: Option<&Box>, 28 | ) { 29 | self.label.set_widget_name(name); 30 | ui::add_and_align(&self.label, align, left, centered, right, box_holder); 31 | cava::CAVA_INSTANCES 32 | .lock() 33 | .expect(ERR_ACCESS_CAVA_INSTANCES) 34 | .push(self); 35 | } 36 | 37 | fn update_label_direct(&self, new_content: &str) { 38 | // Only redraw if the text wasn't the exact same as final_content. 39 | if self.label.text() != new_content { 40 | self.label.set_text(new_content) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/widgets/label_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | config, 3 | constants::{ 4 | ERR_NO_LINES, ERR_STRING_NONE, ERR_TAKE_STDOUT, ERR_WRONG_LABEL_RANIM, PROC_TARGET, 5 | }, 6 | ui, 7 | utils::aliases::use_aliases, 8 | widget::{Align, HWidget}, 9 | }; 10 | use gtk::{glib::GString, traits::*, *}; 11 | use std::{mem::take, process::Stdio, sync::Mutex, time::Duration}; 12 | use tokio::{ 13 | io::{AsyncBufReadExt, BufReader}, 14 | process::Command, 15 | task, 16 | }; 17 | 18 | lazy_static! { 19 | /// Current text buffer from `stdout`. 20 | static ref BUFFER: Mutex = Mutex::new(String::default()); 21 | } 22 | 23 | /// Creates a new label widget. 24 | #[derive(Debug)] 25 | pub struct LabelWidget { 26 | pub tooltip: String, 27 | pub tooltip_command: String, 28 | pub text: String, 29 | pub command: String, 30 | pub update_rate: u64, 31 | pub label: Label, 32 | pub listen: bool, 33 | pub revealer: Revealer, 34 | pub update_anim: Option, 35 | pub anim_duration: u32, 36 | } 37 | 38 | /// 0.3.2: If `listen` is `true`, call this function and then set the label text-value 39 | /// to that of `BUFFER`. 40 | fn begin_listen(cmd: String) { 41 | task::spawn(async move { 42 | let mut child = Command::new(PROC_TARGET) 43 | .args(["-c", &cmd]) 44 | .stdout(Stdio::piped()) 45 | .kill_on_drop(true) 46 | .spawn() 47 | .unwrap_or_else(|_| panic!("[ERROR] Cannot start '{cmd}'!")); 48 | 49 | let out = child.stdout.take().expect(ERR_TAKE_STDOUT); 50 | 51 | let mut reader = BufReader::new(out).lines(); 52 | let update_rate = config::get_update_rate(); 53 | loop { 54 | *BUFFER.lock().unwrap() = reader 55 | .next_line() 56 | .await 57 | .expect(ERR_NO_LINES) 58 | .expect(ERR_STRING_NONE); 59 | 60 | tokio::time::sleep(Duration::from_millis(update_rate)).await; 61 | } 62 | }); 63 | } 64 | 65 | /// Starts updating the dynamic tooltip, if any. 66 | fn start_tooltip_loop(label_ref: &mut LabelWidget) { 67 | if label_ref.tooltip_command.is_empty() { 68 | // Not eligible, cancel. 69 | return; 70 | } 71 | 72 | let label = label_ref.label.to_owned(); 73 | let tooltip = take(&mut label_ref.tooltip); 74 | let tooltip_command = take(&mut label_ref.tooltip_command); 75 | let tick = move || { 76 | let mut new_tooltip = String::default(); 77 | new_tooltip.push_str(&tooltip); 78 | new_tooltip.push_str(&use_aliases(&tooltip_command)); 79 | 80 | let tooltip_markup = label.tooltip_markup().unwrap_or_else(|| GString::from("")); 81 | if !tooltip_markup.eq(&new_tooltip) { 82 | // Markup support here, the user therefore has to deal with any upcoming issues due to 83 | // the command output, on their own. 84 | label.set_tooltip_markup(Some(&new_tooltip)); 85 | } 86 | 87 | glib::Continue(true) 88 | }; 89 | 90 | tick(); 91 | glib::timeout_add_local(Duration::from_millis(1000), tick); 92 | } 93 | 94 | /// Starts updating the dynamic label content. 95 | fn start_label_loop(label_ref: &mut LabelWidget) { 96 | let label = take(&mut label_ref.label); 97 | let command = label_ref.command.to_owned(); 98 | if command.is_empty() || label_ref.update_rate <= 3 { 99 | // Not eligible, cancel. 100 | return; 101 | } 102 | 103 | let text = label_ref.text.to_owned(); 104 | let listen = label_ref.listen; 105 | let update_anim = take(&mut label_ref.update_anim).expect(ERR_WRONG_LABEL_RANIM); 106 | let revealer = take(&mut label_ref.revealer); 107 | let anim_speed = label_ref.anim_duration; 108 | let tick = move || { 109 | if !listen { 110 | let mut new_text = String::default(); 111 | new_text.push_str(&text); 112 | new_text.push_str(&use_aliases(&command)); 113 | 114 | if !label.text().eq(&new_text) { 115 | restart_revealer!( 116 | revealer, 117 | || label.set_text(&new_text), 118 | update_anim, 119 | anim_speed 120 | ); 121 | } 122 | } else { 123 | restart_revealer!( 124 | revealer, 125 | || update_from_buffer(&label), 126 | update_anim, 127 | anim_speed 128 | ); 129 | } 130 | 131 | glib::Continue(true) 132 | }; 133 | 134 | tick(); 135 | glib::timeout_add_local(Duration::from_millis(label_ref.update_rate), tick); 136 | } 137 | 138 | /// Updates the labels content with the string from `BUFFER`. 139 | fn update_from_buffer(label: &Label) { 140 | if let Ok(new_content) = BUFFER.lock() { 141 | let old_content = label.text(); 142 | // eq-check the new content for old_content. Doing the opposite requires a .to_string() 143 | // call. 144 | if !new_content.eq(&old_content) { 145 | // Not the same; set content and redraw. 146 | label.set_text(&new_content); 147 | } 148 | } else { 149 | log!(format!( 150 | "[WARN] Failed retrieving content from BUFFER for label '{}'!", 151 | label.widget_name() 152 | )) 153 | } 154 | } 155 | 156 | // Implements HWidget for the widget so that we can actually use it. 157 | impl HWidget for LabelWidget { 158 | fn add( 159 | mut self, 160 | name: &str, 161 | align: Align, 162 | left: &Box, 163 | centered: &Box, 164 | right: &Box, 165 | box_holder: Option<&Box>, 166 | ) { 167 | let is_static = self.command.is_empty() || self.update_rate == 0; 168 | self.label.set_widget_name(name); 169 | self.label.set_markup(&self.text); 170 | self.label.set_tooltip_markup(Some(&self.tooltip)); 171 | self.revealer.set_child(Some(&self.label)); 172 | self.revealer 173 | .set_transition_type(self.update_anim.expect(ERR_WRONG_LABEL_RANIM)); 174 | ui::add_and_align(&self.revealer, align, left, centered, right, box_holder); 175 | 176 | // 0.4.9: If the reveal_anim is unset, None or the label is static, then reveal instantly. 177 | if self.update_anim.is_none() 178 | || self.update_anim == Some(RevealerTransitionType::None) 179 | || is_static 180 | { 181 | self.revealer.set_reveal_child(true); 182 | } 183 | 184 | if !is_static { 185 | if self.listen { 186 | begin_listen(self.command.to_owned()); 187 | } 188 | 189 | self.start_loop(); 190 | } 191 | 192 | log!(format!( 193 | "Added a new label widget named '{name}', static: {is_static}" 194 | )); 195 | } 196 | 197 | fn start_loop(&mut self) { 198 | // Start loops. 199 | start_tooltip_loop(self); 200 | start_label_loop(self); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/widgets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod box_widget; 2 | pub mod button_widget; 3 | pub mod cava_widget; 4 | pub mod label_widget; 5 | pub mod spacing_widget; 6 | pub mod tray_widget; 7 | -------------------------------------------------------------------------------- /src/widgets/spacing_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ui, 3 | widget::{Align, HWidget}, 4 | }; 5 | use gtk::{traits::*, *}; 6 | 7 | /// Creates a new basic spacing widget. 8 | pub struct SpacingWidget { 9 | pub spacing_start: i32, 10 | pub spacing_end: i32, 11 | } 12 | 13 | // Implements HWidget for the widget so that we can actually use it. 14 | impl HWidget for SpacingWidget { 15 | fn add( 16 | self, 17 | name: &str, 18 | align: Align, 19 | left: &Box, 20 | centered: &Box, 21 | right: &Box, 22 | box_holder: Option<&Box>, 23 | ) { 24 | let widget = Box::new(Orientation::Horizontal, 0); 25 | // 0.2.2: Allow for named spacings 26 | widget.set_widget_name(name); 27 | widget.set_margin_start(self.spacing_start); 28 | widget.set_margin_end(self.spacing_end); 29 | 30 | ui::add_and_align(&widget, align, left, centered, right, box_holder); 31 | log!("Added a new spacing widget"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/widgets/tray_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::{ERR_CREATE_RT, ERR_SEND_MSG_UI}, 3 | ui, 4 | widget::{Align, HWidget}, 5 | }; 6 | use gtk::{ 7 | traits::*, IconLookupFlags, IconTheme, Image, Menu, MenuBar, MenuItem, SeparatorMenuItem, 8 | }; 9 | use std::{collections::HashMap, sync::Mutex, thread}; 10 | use stray::{ 11 | message::{ 12 | menu::{MenuType, TrayMenu}, 13 | tray::{IconPixmap, StatusNotifierItem}, 14 | NotifierItemCommand, 15 | }, 16 | NotifierItemMessage, StatusNotifierWatcher, 17 | }; 18 | use tokio::{runtime::Runtime, sync::mpsc}; 19 | 20 | struct NotifierItem { 21 | item: StatusNotifierItem, 22 | menu: Option, 23 | } 24 | 25 | pub struct StatusNotifierWrapper { 26 | menu: stray::message::menu::MenuItem, 27 | } 28 | 29 | lazy_static! { 30 | static ref STATE: Mutex> = Mutex::new(HashMap::new()); 31 | } 32 | 33 | // TODO: Document this code more and potentially clean it up. 34 | 35 | impl StatusNotifierWrapper { 36 | /// Converts the content into a `MenuItem` 37 | fn into_menu_item( 38 | self, 39 | sender: mpsc::Sender, 40 | notifier_address: String, 41 | menu_path: String, 42 | ) -> MenuItem { 43 | let item: Box> = match self.menu.menu_type { 44 | MenuType::Separator => Box::new(SeparatorMenuItem::new()), 45 | MenuType::Standard => Box::new(MenuItem::with_label(self.menu.label.as_str())), 46 | }; 47 | 48 | let item = (*item).as_ref().clone(); 49 | 50 | { 51 | let sender = sender.clone(); 52 | let notifier_address = notifier_address.clone(); 53 | let menu_path = menu_path.clone(); 54 | 55 | item.connect_activate(move |_item| { 56 | sender 57 | .try_send(NotifierItemCommand::MenuItemClicked { 58 | submenu_id: self.menu.id, 59 | menu_path: menu_path.clone(), 60 | notifier_address: notifier_address.clone(), 61 | }) 62 | .unwrap(); 63 | }); 64 | }; 65 | 66 | let submenu = Menu::new(); 67 | if !self.menu.submenu.is_empty() { 68 | for submenu_item in self.menu.submenu.iter().cloned() { 69 | let submenu_item = StatusNotifierWrapper { menu: submenu_item }; 70 | let submenu_item = submenu_item.into_menu_item( 71 | sender.clone(), 72 | notifier_address.clone(), 73 | menu_path.clone(), 74 | ); 75 | submenu.append(&submenu_item); 76 | } 77 | 78 | item.set_submenu(Some(&submenu)); 79 | } 80 | 81 | item 82 | } 83 | } 84 | 85 | impl NotifierItem { 86 | /// Gets the icon for this tray item. 87 | fn get_icon(&self) -> Option { 88 | match &self.item.icon_pixmap { 89 | None => self.get_icon_from_theme(), 90 | Some(pixmaps) => self.get_icon_from_pixmaps(pixmaps), 91 | } 92 | } 93 | 94 | fn get_icon_from_pixmaps(&self, pixmaps: &[IconPixmap]) -> Option { 95 | let pixmap = pixmaps 96 | .iter() 97 | .find(|pm| pm.height > 20 && pm.height < 32) 98 | .expect("No icon of suitable size found"); 99 | 100 | let pixbuf = gtk::gdk_pixbuf::Pixbuf::new( 101 | gtk::gdk_pixbuf::Colorspace::Rgb, 102 | true, 103 | 8, 104 | pixmap.width, 105 | pixmap.height, 106 | ) 107 | .expect("Failed to allocate pixbuf"); 108 | 109 | for y in 0..pixmap.height { 110 | for x in 0..pixmap.width { 111 | let index = (y * pixmap.width + x) * 4; 112 | let a = pixmap.pixels[index as usize]; 113 | let r = pixmap.pixels[(index + 1) as usize]; 114 | let g = pixmap.pixels[(index + 2) as usize]; 115 | let b = pixmap.pixels[(index + 3) as usize]; 116 | pixbuf.put_pixel(x as u32, y as u32, r, g, b, a); 117 | } 118 | } 119 | 120 | Some(Image::from_pixbuf(Some(&pixbuf))) 121 | } 122 | 123 | /// Tries to get the application icon from the current icon theme. 124 | fn get_icon_from_theme(&self) -> Option { 125 | let theme = gtk::IconTheme::default().unwrap_or_else(IconTheme::new); 126 | theme.rescan_if_needed(); 127 | 128 | if let Some(path) = self.item.icon_theme_path.as_ref() { 129 | theme.append_search_path(path); 130 | } 131 | 132 | let icon_name = self.item.icon_name.as_ref().unwrap(); 133 | let icon = theme.lookup_icon(icon_name, 16, IconLookupFlags::GENERIC_FALLBACK); 134 | 135 | icon.map(|i| Image::from_pixbuf(i.load_icon().ok().as_ref())) 136 | } 137 | } 138 | 139 | /// Creates a new Tray widget. 140 | pub struct TrayWidget; 141 | 142 | // Implements HWidget for the widget so that we can actually use it. 143 | impl HWidget for TrayWidget { 144 | fn add( 145 | self, 146 | name: &str, 147 | align: Align, 148 | left: >k::Box, 149 | centered: >k::Box, 150 | right: >k::Box, 151 | box_holder: Option<>k::Box>, 152 | ) { 153 | if !is_feature_active!("tray_experimental") { 154 | return; 155 | } 156 | 157 | let menu_bar = MenuBar::new(); 158 | menu_bar.set_widget_name(name); 159 | ui::add_and_align(&menu_bar, align, left, centered, right, box_holder); 160 | let (sender, receiver) = mpsc::channel(32); 161 | let (cmd_tx, cmd_rx) = mpsc::channel(32); 162 | 163 | spawn_local_handler(menu_bar, receiver, cmd_tx); 164 | start_communication_thread(sender, cmd_rx); 165 | } 166 | } 167 | 168 | fn spawn_local_handler( 169 | v_box: MenuBar, 170 | mut receiver: mpsc::Receiver, 171 | cmd_tx: mpsc::Sender, 172 | ) { 173 | let main_context = glib::MainContext::default(); 174 | let future = async move { 175 | while let Some(item) = receiver.recv().await { 176 | let mut state = STATE.lock().unwrap(); 177 | 178 | match item { 179 | NotifierItemMessage::Update { 180 | address: id, 181 | item, 182 | menu, 183 | } => { 184 | state.insert(id, NotifierItem { item: *item, menu }); 185 | } 186 | NotifierItemMessage::Remove { address } => { 187 | state.remove(&address); 188 | } 189 | } 190 | 191 | for child in v_box.children() { 192 | v_box.remove(&child); 193 | } 194 | 195 | for (address, notifier_item) in state.iter() { 196 | if let Some(ref icon) = notifier_item.get_icon() { 197 | // Create the menu 198 | 199 | let menu_item = MenuItem::new(); 200 | let menu_item_box = gtk::Box::default(); 201 | menu_item_box.add(icon); 202 | menu_item.add(&menu_item_box); 203 | 204 | if let Some(tray_menu) = ¬ifier_item.menu { 205 | let menu = Menu::new(); 206 | tray_menu 207 | .submenus 208 | .iter() 209 | .map(|submenu| StatusNotifierWrapper { 210 | menu: submenu.to_owned(), 211 | }) 212 | .map(|item| { 213 | let menu_path = 214 | notifier_item.item.menu.as_ref().unwrap().to_string(); 215 | let address = address.to_string(); 216 | item.into_menu_item(cmd_tx.clone(), address, menu_path) 217 | }) 218 | .for_each(|item| menu.append(&item)); 219 | 220 | if !tray_menu.submenus.is_empty() { 221 | // Has items in the sub menu. 222 | menu_item.set_submenu(Some(&menu)); 223 | } 224 | } 225 | v_box.append(&menu_item); 226 | }; 227 | 228 | v_box.show_all(); 229 | } 230 | } 231 | }; 232 | 233 | main_context.spawn_local(future); 234 | } 235 | 236 | fn start_communication_thread( 237 | sender: mpsc::Sender, 238 | cmd_rx: mpsc::Receiver, 239 | ) { 240 | thread::spawn(move || { 241 | let runtime = Runtime::new().expect(ERR_CREATE_RT); 242 | 243 | runtime.block_on(async { 244 | let tray = StatusNotifierWatcher::new(cmd_rx).await.unwrap(); 245 | let mut host = tray.create_notifier_host("Hybrid").await.unwrap(); 246 | 247 | while let Ok(message) = host.recv().await { 248 | sender.send(message).await.expect(ERR_SEND_MSG_UI); 249 | } 250 | 251 | host.destroy().await.unwrap(); 252 | }) 253 | }); 254 | } 255 | --------------------------------------------------------------------------------