├── .envrc ├── .github └── workflows │ ├── code_style.yml │ ├── linux.yml │ └── security_check.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE-2.0 ├── LICENSE-MIT ├── Makefile ├── README.md ├── data ├── glowing-logo │ ├── animation.toml │ ├── part1.mp4 │ └── part2.mp4 └── ossystems-demo │ ├── animation.toml │ ├── beginning.mp4 │ ├── end.mp4 │ └── intermediate.mp4 ├── doc └── demo-animation.gif ├── etc ├── easysplash-quit.service.in ├── easysplash-start.init.in ├── easysplash-start.service.in └── easysplash.default ├── flake.lock ├── flake.nix ├── rustfmt.toml └── src ├── animation.rs ├── gstreamer.rs ├── main.rs └── message.rs /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/code_style.yml: -------------------------------------------------------------------------------- 1 | name: Code Style Check 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | clippy_check: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - name: Install Dependencies 14 | run: | 15 | sudo apt update 16 | sudo apt install -f libgstreamer1.0-dev libsystemd-dev 17 | - uses: actions/checkout@v2 18 | - uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: nightly 21 | override: true 22 | components: rustfmt, clippy 23 | 24 | - name: Run cargo fmt 25 | uses: actions-rs/cargo@v1 26 | with: 27 | command: fmt 28 | args: --all -- --check 29 | 30 | - uses: actions-rs/clippy-check@v1 31 | with: 32 | token: ${{ secrets.GITHUB_TOKEN }} 33 | args: --all-features --all --tests 34 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: CI (Linux) 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build_and_test: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | version: 15 | - 1.66.0 # MSRV 16 | - stable 17 | - nightly 18 | 19 | name: Test ${{ matrix.version }} - x86_64-unknown-linux-gnu 20 | runs-on: ubuntu-20.04 21 | 22 | steps: 23 | - name: Install Dependencies 24 | run: | 25 | sudo apt update 26 | sudo apt-get install -f libgstreamer1.0-dev libsystemd-dev 27 | - name: Checkout sources 28 | uses: actions/checkout@v2 29 | - name: Install ${{ matrix.version }} 30 | uses: actions-rs/toolchain@v1 31 | with: 32 | toolchain: ${{ matrix.version }}-x86_64-unknown-linux-gnu 33 | profile: minimal 34 | override: true 35 | 36 | - name: Generate Cargo.lock 37 | uses: actions-rs/cargo@v1 38 | with: 39 | command: generate-lockfile 40 | - name: Cache cargo registry 41 | uses: actions/cache@v1 42 | with: 43 | path: ~/.cargo/registry 44 | key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} 45 | - name: Cache cargo index 46 | uses: actions/cache@v1 47 | with: 48 | path: ~/.cargo/git 49 | key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} 50 | - name: Cache cargo build 51 | uses: actions/cache@v1 52 | with: 53 | path: target 54 | key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} 55 | 56 | - name: Check build 57 | uses: actions-rs/cargo@v1 58 | with: 59 | command: check 60 | args: --release --all --bins --examples --tests 61 | 62 | - name: Tests 63 | uses: actions-rs/cargo@v1 64 | timeout-minutes: 10 65 | with: 66 | command: test 67 | args: --release --all --all-features --no-fail-fast -- --nocapture 68 | 69 | - name: Clear the cargo caches 70 | run: | 71 | cargo install cargo-cache --no-default-features --features ci-autoclean 72 | cargo-cache 73 | -------------------------------------------------------------------------------- /.github/workflows/security_check.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | push: 6 | paths: 7 | - '**/Cargo.toml' 8 | - '**/Cargo.lock' 9 | pull_request: 10 | 11 | jobs: 12 | audit: 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions-rs/audit-check@v1 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /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 = "anyhow" 7 | version = "1.0.70" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" 10 | 11 | [[package]] 12 | name = "argh" 13 | version = "0.1.10" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e" 16 | dependencies = [ 17 | "argh_derive", 18 | "argh_shared", 19 | ] 20 | 21 | [[package]] 22 | name = "argh_derive" 23 | version = "0.1.10" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6" 26 | dependencies = [ 27 | "argh_shared", 28 | "proc-macro2", 29 | "quote", 30 | "syn 1.0.109", 31 | ] 32 | 33 | [[package]] 34 | name = "argh_shared" 35 | version = "0.1.10" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" 38 | 39 | [[package]] 40 | name = "async-attributes" 41 | version = "1.1.2" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" 44 | dependencies = [ 45 | "quote", 46 | "syn 1.0.109", 47 | ] 48 | 49 | [[package]] 50 | name = "async-channel" 51 | version = "1.6.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 54 | dependencies = [ 55 | "concurrent-queue", 56 | "event-listener", 57 | "futures-core", 58 | ] 59 | 60 | [[package]] 61 | name = "async-executor" 62 | version = "1.4.1" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 65 | dependencies = [ 66 | "async-task", 67 | "concurrent-queue", 68 | "fastrand", 69 | "futures-lite", 70 | "once_cell", 71 | "slab", 72 | ] 73 | 74 | [[package]] 75 | name = "async-global-executor" 76 | version = "2.2.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" 79 | dependencies = [ 80 | "async-channel", 81 | "async-executor", 82 | "async-io", 83 | "async-lock", 84 | "blocking", 85 | "futures-lite", 86 | "num_cpus", 87 | "once_cell", 88 | ] 89 | 90 | [[package]] 91 | name = "async-io" 92 | version = "1.7.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" 95 | dependencies = [ 96 | "concurrent-queue", 97 | "futures-lite", 98 | "libc", 99 | "log", 100 | "once_cell", 101 | "parking", 102 | "polling", 103 | "slab", 104 | "socket2", 105 | "waker-fn", 106 | "winapi", 107 | ] 108 | 109 | [[package]] 110 | name = "async-lock" 111 | version = "2.5.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" 114 | dependencies = [ 115 | "event-listener", 116 | ] 117 | 118 | [[package]] 119 | name = "async-process" 120 | version = "1.4.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" 123 | dependencies = [ 124 | "async-io", 125 | "blocking", 126 | "cfg-if", 127 | "event-listener", 128 | "futures-lite", 129 | "libc", 130 | "once_cell", 131 | "signal-hook", 132 | "winapi", 133 | ] 134 | 135 | [[package]] 136 | name = "async-std" 137 | version = "1.12.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" 140 | dependencies = [ 141 | "async-attributes", 142 | "async-channel", 143 | "async-global-executor", 144 | "async-io", 145 | "async-lock", 146 | "async-process", 147 | "crossbeam-utils", 148 | "futures-channel", 149 | "futures-core", 150 | "futures-io", 151 | "futures-lite", 152 | "gloo-timers", 153 | "kv-log-macro", 154 | "log", 155 | "memchr", 156 | "once_cell", 157 | "pin-project-lite", 158 | "pin-utils", 159 | "slab", 160 | "wasm-bindgen-futures", 161 | ] 162 | 163 | [[package]] 164 | name = "async-task" 165 | version = "4.3.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" 168 | 169 | [[package]] 170 | name = "atomic-waker" 171 | version = "1.0.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 174 | 175 | [[package]] 176 | name = "autocfg" 177 | version = "1.1.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 180 | 181 | [[package]] 182 | name = "bitflags" 183 | version = "1.3.2" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 186 | 187 | [[package]] 188 | name = "blocking" 189 | version = "1.2.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" 192 | dependencies = [ 193 | "async-channel", 194 | "async-task", 195 | "atomic-waker", 196 | "fastrand", 197 | "futures-lite", 198 | "once_cell", 199 | ] 200 | 201 | [[package]] 202 | name = "build-env" 203 | version = "0.3.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "1522ac6ee801a11bf9ef3f80403f4ede6eb41291fac3dde3de09989679305f25" 206 | 207 | [[package]] 208 | name = "bumpalo" 209 | version = "3.10.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" 212 | 213 | [[package]] 214 | name = "cache-padded" 215 | version = "1.2.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" 218 | 219 | [[package]] 220 | name = "cc" 221 | version = "1.0.73" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 224 | 225 | [[package]] 226 | name = "cfg-expr" 227 | version = "0.15.1" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9" 230 | dependencies = [ 231 | "smallvec", 232 | "target-lexicon", 233 | ] 234 | 235 | [[package]] 236 | name = "cfg-if" 237 | version = "1.0.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 240 | 241 | [[package]] 242 | name = "concurrent-queue" 243 | version = "1.2.4" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" 246 | dependencies = [ 247 | "cache-padded", 248 | ] 249 | 250 | [[package]] 251 | name = "crossbeam-utils" 252 | version = "0.8.11" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" 255 | dependencies = [ 256 | "cfg-if", 257 | "once_cell", 258 | ] 259 | 260 | [[package]] 261 | name = "cstr-argument" 262 | version = "0.1.2" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "b6bd9c8e659a473bce955ae5c35b116af38af11a7acb0b480e01f3ed348aeb40" 265 | dependencies = [ 266 | "cfg-if", 267 | "memchr", 268 | ] 269 | 270 | [[package]] 271 | name = "derive_more" 272 | version = "0.99.17" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 275 | dependencies = [ 276 | "proc-macro2", 277 | "quote", 278 | "syn 1.0.109", 279 | ] 280 | 281 | [[package]] 282 | name = "easysplash" 283 | version = "1.90.0" 284 | dependencies = [ 285 | "anyhow", 286 | "argh", 287 | "async-std", 288 | "derive_more", 289 | "gstreamer", 290 | "log", 291 | "serde", 292 | "simple-logging", 293 | "systemd", 294 | "toml", 295 | ] 296 | 297 | [[package]] 298 | name = "event-listener" 299 | version = "2.5.3" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 302 | 303 | [[package]] 304 | name = "fastrand" 305 | version = "1.8.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 308 | dependencies = [ 309 | "instant", 310 | ] 311 | 312 | [[package]] 313 | name = "foreign-types" 314 | version = "0.5.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 317 | dependencies = [ 318 | "foreign-types-macros", 319 | "foreign-types-shared", 320 | ] 321 | 322 | [[package]] 323 | name = "foreign-types-macros" 324 | version = "0.2.2" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc" 327 | dependencies = [ 328 | "proc-macro2", 329 | "quote", 330 | "syn 1.0.109", 331 | ] 332 | 333 | [[package]] 334 | name = "foreign-types-shared" 335 | version = "0.3.1" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 338 | 339 | [[package]] 340 | name = "futures-channel" 341 | version = "0.3.28" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" 344 | dependencies = [ 345 | "futures-core", 346 | ] 347 | 348 | [[package]] 349 | name = "futures-core" 350 | version = "0.3.28" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 353 | 354 | [[package]] 355 | name = "futures-executor" 356 | version = "0.3.28" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" 359 | dependencies = [ 360 | "futures-core", 361 | "futures-task", 362 | "futures-util", 363 | ] 364 | 365 | [[package]] 366 | name = "futures-io" 367 | version = "0.3.21" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 370 | 371 | [[package]] 372 | name = "futures-lite" 373 | version = "1.12.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 376 | dependencies = [ 377 | "fastrand", 378 | "futures-core", 379 | "futures-io", 380 | "memchr", 381 | "parking", 382 | "pin-project-lite", 383 | "waker-fn", 384 | ] 385 | 386 | [[package]] 387 | name = "futures-macro" 388 | version = "0.3.28" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" 391 | dependencies = [ 392 | "proc-macro2", 393 | "quote", 394 | "syn 2.0.15", 395 | ] 396 | 397 | [[package]] 398 | name = "futures-task" 399 | version = "0.3.28" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 402 | 403 | [[package]] 404 | name = "futures-util" 405 | version = "0.3.28" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 408 | dependencies = [ 409 | "futures-core", 410 | "futures-macro", 411 | "futures-task", 412 | "pin-project-lite", 413 | "pin-utils", 414 | "slab", 415 | ] 416 | 417 | [[package]] 418 | name = "gio-sys" 419 | version = "0.17.4" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "6b1d43b0d7968b48455244ecafe41192871257f5740aa6b095eb19db78e362a5" 422 | dependencies = [ 423 | "glib-sys", 424 | "gobject-sys", 425 | "libc", 426 | "system-deps", 427 | "winapi", 428 | ] 429 | 430 | [[package]] 431 | name = "glib" 432 | version = "0.17.9" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "a7f1de7cbde31ea4f0a919453a2dcece5d54d5b70e08f8ad254dc4840f5f09b6" 435 | dependencies = [ 436 | "bitflags", 437 | "futures-channel", 438 | "futures-core", 439 | "futures-executor", 440 | "futures-task", 441 | "futures-util", 442 | "gio-sys", 443 | "glib-macros", 444 | "glib-sys", 445 | "gobject-sys", 446 | "libc", 447 | "memchr", 448 | "once_cell", 449 | "smallvec", 450 | "thiserror", 451 | ] 452 | 453 | [[package]] 454 | name = "glib-macros" 455 | version = "0.17.9" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "0a7206c5c03851ef126ea1444990e81fdd6765fb799d5bc694e4897ca01bb97f" 458 | dependencies = [ 459 | "anyhow", 460 | "heck", 461 | "proc-macro-crate", 462 | "proc-macro-error", 463 | "proc-macro2", 464 | "quote", 465 | "syn 1.0.109", 466 | ] 467 | 468 | [[package]] 469 | name = "glib-sys" 470 | version = "0.17.4" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "49f00ad0a1bf548e61adfff15d83430941d9e1bb620e334f779edd1c745680a5" 473 | dependencies = [ 474 | "libc", 475 | "system-deps", 476 | ] 477 | 478 | [[package]] 479 | name = "gloo-timers" 480 | version = "0.2.4" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" 483 | dependencies = [ 484 | "futures-channel", 485 | "futures-core", 486 | "js-sys", 487 | "wasm-bindgen", 488 | ] 489 | 490 | [[package]] 491 | name = "gobject-sys" 492 | version = "0.17.4" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "15e75b0000a64632b2d8ca3cf856af9308e3a970844f6e9659bd197f026793d0" 495 | dependencies = [ 496 | "glib-sys", 497 | "libc", 498 | "system-deps", 499 | ] 500 | 501 | [[package]] 502 | name = "gstreamer" 503 | version = "0.20.5" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "4530401c89be6dc10d77ae1587b811cf455c97dce7abf594cb9164527c7da7fc" 506 | dependencies = [ 507 | "bitflags", 508 | "cfg-if", 509 | "futures-channel", 510 | "futures-core", 511 | "futures-util", 512 | "glib", 513 | "gstreamer-sys", 514 | "libc", 515 | "muldiv", 516 | "num-integer", 517 | "num-rational", 518 | "once_cell", 519 | "option-operations", 520 | "paste", 521 | "pretty-hex", 522 | "smallvec", 523 | "thiserror", 524 | ] 525 | 526 | [[package]] 527 | name = "gstreamer-sys" 528 | version = "0.20.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "e56fe047adef7d47dbafa8bc1340fddb53c325e16574763063702fc94b5786d2" 531 | dependencies = [ 532 | "glib-sys", 533 | "gobject-sys", 534 | "libc", 535 | "system-deps", 536 | ] 537 | 538 | [[package]] 539 | name = "hashbrown" 540 | version = "0.12.3" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 543 | 544 | [[package]] 545 | name = "heck" 546 | version = "0.4.1" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 549 | 550 | [[package]] 551 | name = "hermit-abi" 552 | version = "0.1.19" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 555 | dependencies = [ 556 | "libc", 557 | ] 558 | 559 | [[package]] 560 | name = "indexmap" 561 | version = "1.9.3" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 564 | dependencies = [ 565 | "autocfg", 566 | "hashbrown", 567 | ] 568 | 569 | [[package]] 570 | name = "instant" 571 | version = "0.1.12" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 574 | dependencies = [ 575 | "cfg-if", 576 | ] 577 | 578 | [[package]] 579 | name = "js-sys" 580 | version = "0.3.59" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" 583 | dependencies = [ 584 | "wasm-bindgen", 585 | ] 586 | 587 | [[package]] 588 | name = "kv-log-macro" 589 | version = "1.0.7" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 592 | dependencies = [ 593 | "log", 594 | ] 595 | 596 | [[package]] 597 | name = "lazy_static" 598 | version = "1.4.0" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 601 | 602 | [[package]] 603 | name = "libc" 604 | version = "0.2.142" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" 607 | 608 | [[package]] 609 | name = "libsystemd-sys" 610 | version = "0.9.3" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "ed080163caa59cc29b34bce2209b737149a4bac148cd9a8b04e4c12822798119" 613 | dependencies = [ 614 | "build-env", 615 | "libc", 616 | "pkg-config", 617 | ] 618 | 619 | [[package]] 620 | name = "log" 621 | version = "0.4.18" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" 624 | dependencies = [ 625 | "value-bag", 626 | ] 627 | 628 | [[package]] 629 | name = "memchr" 630 | version = "2.5.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 633 | 634 | [[package]] 635 | name = "muldiv" 636 | version = "1.0.1" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" 639 | 640 | [[package]] 641 | name = "num-integer" 642 | version = "0.1.45" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 645 | dependencies = [ 646 | "autocfg", 647 | "num-traits", 648 | ] 649 | 650 | [[package]] 651 | name = "num-rational" 652 | version = "0.4.1" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 655 | dependencies = [ 656 | "autocfg", 657 | "num-integer", 658 | "num-traits", 659 | ] 660 | 661 | [[package]] 662 | name = "num-traits" 663 | version = "0.2.15" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 666 | dependencies = [ 667 | "autocfg", 668 | ] 669 | 670 | [[package]] 671 | name = "num_cpus" 672 | version = "1.13.1" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 675 | dependencies = [ 676 | "hermit-abi", 677 | "libc", 678 | ] 679 | 680 | [[package]] 681 | name = "once_cell" 682 | version = "1.17.1" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 685 | 686 | [[package]] 687 | name = "option-operations" 688 | version = "0.5.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" 691 | dependencies = [ 692 | "paste", 693 | ] 694 | 695 | [[package]] 696 | name = "parking" 697 | version = "2.0.0" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 700 | 701 | [[package]] 702 | name = "paste" 703 | version = "1.0.12" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" 706 | 707 | [[package]] 708 | name = "pin-project-lite" 709 | version = "0.2.9" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 712 | 713 | [[package]] 714 | name = "pin-utils" 715 | version = "0.1.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 718 | 719 | [[package]] 720 | name = "pkg-config" 721 | version = "0.3.26" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 724 | 725 | [[package]] 726 | name = "polling" 727 | version = "2.2.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" 730 | dependencies = [ 731 | "cfg-if", 732 | "libc", 733 | "log", 734 | "wepoll-ffi", 735 | "winapi", 736 | ] 737 | 738 | [[package]] 739 | name = "pretty-hex" 740 | version = "0.3.0" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" 743 | 744 | [[package]] 745 | name = "proc-macro-crate" 746 | version = "1.3.1" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 749 | dependencies = [ 750 | "once_cell", 751 | "toml_edit", 752 | ] 753 | 754 | [[package]] 755 | name = "proc-macro-error" 756 | version = "1.0.4" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 759 | dependencies = [ 760 | "proc-macro-error-attr", 761 | "proc-macro2", 762 | "quote", 763 | "syn 1.0.109", 764 | "version_check", 765 | ] 766 | 767 | [[package]] 768 | name = "proc-macro-error-attr" 769 | version = "1.0.4" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 772 | dependencies = [ 773 | "proc-macro2", 774 | "quote", 775 | "version_check", 776 | ] 777 | 778 | [[package]] 779 | name = "proc-macro2" 780 | version = "1.0.56" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 783 | dependencies = [ 784 | "unicode-ident", 785 | ] 786 | 787 | [[package]] 788 | name = "quote" 789 | version = "1.0.26" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 792 | dependencies = [ 793 | "proc-macro2", 794 | ] 795 | 796 | [[package]] 797 | name = "redox_syscall" 798 | version = "0.1.57" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 801 | 802 | [[package]] 803 | name = "serde" 804 | version = "1.0.160" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" 807 | dependencies = [ 808 | "serde_derive", 809 | ] 810 | 811 | [[package]] 812 | name = "serde_derive" 813 | version = "1.0.160" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" 816 | dependencies = [ 817 | "proc-macro2", 818 | "quote", 819 | "syn 2.0.15", 820 | ] 821 | 822 | [[package]] 823 | name = "serde_spanned" 824 | version = "0.6.1" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" 827 | dependencies = [ 828 | "serde", 829 | ] 830 | 831 | [[package]] 832 | name = "signal-hook" 833 | version = "0.3.14" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" 836 | dependencies = [ 837 | "libc", 838 | "signal-hook-registry", 839 | ] 840 | 841 | [[package]] 842 | name = "signal-hook-registry" 843 | version = "1.4.0" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 846 | dependencies = [ 847 | "libc", 848 | ] 849 | 850 | [[package]] 851 | name = "simple-logging" 852 | version = "2.0.2" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542" 855 | dependencies = [ 856 | "lazy_static", 857 | "log", 858 | "thread-id", 859 | ] 860 | 861 | [[package]] 862 | name = "slab" 863 | version = "0.4.8" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 866 | dependencies = [ 867 | "autocfg", 868 | ] 869 | 870 | [[package]] 871 | name = "smallvec" 872 | version = "1.10.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 875 | 876 | [[package]] 877 | name = "socket2" 878 | version = "0.4.4" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 881 | dependencies = [ 882 | "libc", 883 | "winapi", 884 | ] 885 | 886 | [[package]] 887 | name = "syn" 888 | version = "1.0.109" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 891 | dependencies = [ 892 | "proc-macro2", 893 | "quote", 894 | "unicode-ident", 895 | ] 896 | 897 | [[package]] 898 | name = "syn" 899 | version = "2.0.15" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" 902 | dependencies = [ 903 | "proc-macro2", 904 | "quote", 905 | "unicode-ident", 906 | ] 907 | 908 | [[package]] 909 | name = "system-deps" 910 | version = "6.0.5" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "d0fe581ad25d11420b873cf9aedaca0419c2b411487b134d4d21065f3d092055" 913 | dependencies = [ 914 | "cfg-expr", 915 | "heck", 916 | "pkg-config", 917 | "toml", 918 | "version-compare", 919 | ] 920 | 921 | [[package]] 922 | name = "systemd" 923 | version = "0.10.0" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "afec0101d9ae8ab26aedf0840109df689938ea7e538aa03df4369f1854f11562" 926 | dependencies = [ 927 | "cstr-argument", 928 | "foreign-types", 929 | "libc", 930 | "libsystemd-sys", 931 | "log", 932 | "memchr", 933 | "utf8-cstr", 934 | ] 935 | 936 | [[package]] 937 | name = "target-lexicon" 938 | version = "0.12.6" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" 941 | 942 | [[package]] 943 | name = "thiserror" 944 | version = "1.0.40" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 947 | dependencies = [ 948 | "thiserror-impl", 949 | ] 950 | 951 | [[package]] 952 | name = "thiserror-impl" 953 | version = "1.0.40" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 956 | dependencies = [ 957 | "proc-macro2", 958 | "quote", 959 | "syn 2.0.15", 960 | ] 961 | 962 | [[package]] 963 | name = "thread-id" 964 | version = "3.3.0" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" 967 | dependencies = [ 968 | "libc", 969 | "redox_syscall", 970 | "winapi", 971 | ] 972 | 973 | [[package]] 974 | name = "toml" 975 | version = "0.7.3" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" 978 | dependencies = [ 979 | "serde", 980 | "serde_spanned", 981 | "toml_datetime", 982 | "toml_edit", 983 | ] 984 | 985 | [[package]] 986 | name = "toml_datetime" 987 | version = "0.6.1" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" 990 | dependencies = [ 991 | "serde", 992 | ] 993 | 994 | [[package]] 995 | name = "toml_edit" 996 | version = "0.19.8" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" 999 | dependencies = [ 1000 | "indexmap", 1001 | "serde", 1002 | "serde_spanned", 1003 | "toml_datetime", 1004 | "winnow", 1005 | ] 1006 | 1007 | [[package]] 1008 | name = "unicode-ident" 1009 | version = "1.0.8" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 1012 | 1013 | [[package]] 1014 | name = "utf8-cstr" 1015 | version = "0.1.6" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "55bcbb425141152b10d5693095950b51c3745d019363fc2929ffd8f61449b628" 1018 | 1019 | [[package]] 1020 | name = "value-bag" 1021 | version = "1.4.0" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" 1024 | 1025 | [[package]] 1026 | name = "version-compare" 1027 | version = "0.1.1" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" 1030 | 1031 | [[package]] 1032 | name = "version_check" 1033 | version = "0.9.4" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1036 | 1037 | [[package]] 1038 | name = "waker-fn" 1039 | version = "1.1.0" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 1042 | 1043 | [[package]] 1044 | name = "wasm-bindgen" 1045 | version = "0.2.82" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" 1048 | dependencies = [ 1049 | "cfg-if", 1050 | "wasm-bindgen-macro", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "wasm-bindgen-backend" 1055 | version = "0.2.82" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" 1058 | dependencies = [ 1059 | "bumpalo", 1060 | "log", 1061 | "once_cell", 1062 | "proc-macro2", 1063 | "quote", 1064 | "syn 1.0.109", 1065 | "wasm-bindgen-shared", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "wasm-bindgen-futures" 1070 | version = "0.4.32" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" 1073 | dependencies = [ 1074 | "cfg-if", 1075 | "js-sys", 1076 | "wasm-bindgen", 1077 | "web-sys", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "wasm-bindgen-macro" 1082 | version = "0.2.82" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" 1085 | dependencies = [ 1086 | "quote", 1087 | "wasm-bindgen-macro-support", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "wasm-bindgen-macro-support" 1092 | version = "0.2.82" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" 1095 | dependencies = [ 1096 | "proc-macro2", 1097 | "quote", 1098 | "syn 1.0.109", 1099 | "wasm-bindgen-backend", 1100 | "wasm-bindgen-shared", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "wasm-bindgen-shared" 1105 | version = "0.2.82" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" 1108 | 1109 | [[package]] 1110 | name = "web-sys" 1111 | version = "0.3.59" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" 1114 | dependencies = [ 1115 | "js-sys", 1116 | "wasm-bindgen", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "wepoll-ffi" 1121 | version = "0.1.2" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" 1124 | dependencies = [ 1125 | "cc", 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "winapi" 1130 | version = "0.3.9" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1133 | dependencies = [ 1134 | "winapi-i686-pc-windows-gnu", 1135 | "winapi-x86_64-pc-windows-gnu", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "winapi-i686-pc-windows-gnu" 1140 | version = "0.4.0" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1143 | 1144 | [[package]] 1145 | name = "winapi-x86_64-pc-windows-gnu" 1146 | version = "0.4.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1149 | 1150 | [[package]] 1151 | name = "winnow" 1152 | version = "0.4.1" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" 1155 | dependencies = [ 1156 | "memchr", 1157 | ] 1158 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # EasySplash - tool for animated splash screens 2 | # Copyright (C) 2020 O.S. Systems Software LTDA. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 OR MIT 5 | 6 | [package] 7 | name = "easysplash" 8 | version = "1.90.0" 9 | authors = ["Otavio Salvador "] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | anyhow = "1.0.70" 14 | argh = "0.1.10" 15 | async-std = { version = "1.12.0", features = ["attributes", "unstable"] } 16 | derive_more = { version = "0.99.17", default-features = false, features = ["display", "from", "error"] } 17 | gst = { version = "0.20.5", package = "gstreamer", default-features = false } 18 | log = { version = "0.4.18", default-features = false } 19 | serde = { version = "1.0.160", features = ["derive"] } 20 | simple-logging = "2.0.2" 21 | toml = "0.7.3" 22 | 23 | # Optional dependencies 24 | systemd = { version = "0.10.0", optional = true } 25 | 26 | [features] 27 | default = [] 28 | 29 | [profile.release] 30 | opt-level = 'z' # Optimize for size. 31 | lto = true 32 | codegen-units = 1 33 | -------------------------------------------------------------------------------- /LICENSE-APACHE-2.0: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prefix ?= /usr/local 2 | sysconfdir ?= /etc 3 | sbindir ?= $(prefix)/sbin 4 | base_libdir ?= /lib 5 | 6 | SRC = Makefile Cargo.lock Cargo.toml $(shell find src -type f -wholename '*src/*.rs') 7 | 8 | BIN = easysplash 9 | DEFAULT_ANIMATION ?= ossystems-demo 10 | 11 | SYSTEMD ?= 0 12 | ifeq ($(SYSTEMD),1) 13 | PKG_CONFIG ?= pkg-config 14 | systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd); 15 | ARGS += --features systemd 16 | ifeq ($(strip $(systemdsystemunitdir)),) 17 | $(error Could not find systemd.pc file. Aborting)) 18 | endif 19 | endif 20 | 21 | INITD ?= 0 22 | 23 | DEBUG ?= 0 24 | TARGET = debug 25 | ifeq ($(DEBUG),0) 26 | ARGS += "--release" 27 | TARGET = release 28 | endif 29 | 30 | BINARY=target/$(TARGET)/$(BIN) 31 | 32 | .PHONY: all clean distclean install uninstall update 33 | 34 | all: $(BINARY) install 35 | 36 | clean: 37 | cargo clean 38 | 39 | distclean: 40 | rm -rf .cargo target 41 | 42 | install: install-binary install-service install-glowing-animation install-ossystems-animation 43 | 44 | install-binary: 45 | @install -Dm04755 "$(BINARY)" "$(DESTDIR)$(sbindir)/$(BIN)" 46 | 47 | install-service: 48 | @install -Dm0644 "etc/easysplash.default" "$(DESTDIR)$(sysconfdir)/default/easysplash" 49 | 50 | @if [ "$(SYSTEMD)" = "1" ]; then \ 51 | install -Dm0644 "etc/easysplash-start.service.in" "$(DESTDIR)$(systemdsystemunitdir)/easysplash-start.service"; \ 52 | install -Dm0644 "etc/easysplash-quit.service.in" "$(DESTDIR)$(systemdsystemunitdir)/easysplash-quit.service"; \ 53 | fi 54 | 55 | @if [ "$(INITD)" = "1" ]; then \ 56 | install -Dm0755 "etc/easysplash-start.init.in" "$(DESTDIR)$(sysconfdir)/init.d/easysplash-start"; \ 57 | fi 58 | 59 | @for script in "$(DESTDIR)$(systemdsystemunitdir)/easysplash-start.service" \ 60 | "$(DESTDIR)$(systemdsystemunitdir)/easysplash-quit.service" \ 61 | "$(DESTDIR)$(sysconfdir)/init.d/easysplash-start"; do \ 62 | if [ -e $$script ]; then \ 63 | sed -e "s,@SYSCONFDIR@,$(sysconfdir),g" \ 64 | -e "s,@SBINDIR@,$(sbindir),g" \ 65 | -i $$script; \ 66 | fi; \ 67 | done 68 | 69 | install-glowing-animation: 70 | @mkdir -p "$(DESTDIR)$(base_libdir)/easysplash/" 71 | @cp -r data/glowing-logo "$(DESTDIR)$(base_libdir)/easysplash/" 72 | @ln -sf "$(base_libdir)/easysplash/$(DEFAULT_ANIMATION)" "$(DESTDIR)$(base_libdir)/easysplash/animation" 73 | 74 | install-ossystems-animation: 75 | @mkdir -p "$(DESTDIR)$(base_libdir)/easysplash/" 76 | @cp -r data/ossystems-demo "$(DESTDIR)$(base_libdir)/easysplash/" 77 | @ln -sf "$(base_libdir)/easysplash/$(DEFAULT_ANIMATION)" "$(DESTDIR)$(base_libdir)/easysplash/animation" 78 | 79 | $(BINARY): 80 | cargo build $(ARGS) 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EasySplash 2 | ========== 3 | 4 | EasySplash is an application that runs early the OS boot for showing graphical animation while the 5 | boot process itself happens in the background. 6 | 7 | The app is built on Rust and binds to GStreamer, so it can run in any backend that supports both. 8 | It can also be used for debugging and testing new animations on the desktop before deploying it to 9 | embedded devices. 10 | 11 | For example, the below is the animation that has been in use for O.S. Systems' demo images: 12 | 13 | ![O.S. Systems demo boot animation](https://github.com/OSSystems/easysplash/raw/master/doc/demo-animation.gif) 14 | 15 | There are two animation [examples](https://github.com/OSSystems/EasySplash/tree/master/data), which may be used as reference: 16 | 17 | * [O.S. Systems glowing logo](https://github.com/OSSystems/EasySplash/tree/master/data/glowing-logo/) 18 | * [O.S. Systems demo boot animation](https://github.com/OSSystems/EasySplash/tree/master/data/ossystems-demo/) 19 | 20 | 21 | Requirements 22 | ------------ 23 | 24 | * Rust 1.66.0 or newer 25 | * GStreamer (tested with 1.16) 26 | 27 | 28 | Running EasySplash 29 | ------------------ 30 | 31 | This is the help screen of EasySplash when ran with the -h argument: 32 | 33 | Usage: easysplash [] 34 | 35 | EasySplash offers a convenient boot splash for Embedded Linux devices, 36 | focusing of simplicity and easy to use. 37 | 38 | Options: 39 | --help display usage information 40 | 41 | Commands: 42 | open open the render with the specific animation 43 | client control the render from the user space 44 | 45 | Boot parameters: 46 | easysplash.enable If you want to skip the EasySplash on system boot set up 47 | this to "0". 48 | 49 | Animation format 50 | ---------------- 51 | 52 | EasySplash accepts animations in a directory which layout must be like this: 53 | 54 | animation.toml 55 | part1.mp4 56 | part2.mp4 57 | part3.mp4 58 | 59 | The names of the animation files can be chosen freely. Only the animation manifest file, named 60 | `animation.toml`, needs to always be named as shown. 61 | 62 | The `animation.toml` file uses the [TOML](https://github.com/toml-lang/toml) format and intends to 63 | define the animation. Below is a full example, for reference: 64 | 65 | [[part]] 66 | file = "part1.mp4" 67 | mode = "complete" 68 | 69 | [[part]] 70 | file = "part2.mp4" 71 | mode = "complete" 72 | repeat = 1 73 | 74 | [[part]] 75 | file = "part3.mp4" 76 | mode = "interruptable" 77 | 78 | The animation view port is always rendered on the center of screen. The parts are defined using 79 | `[[part]]`. The parts are inserted in the order encountered. For every part, following options are 80 | available: 81 | 82 | - `file`: specifies the file to use for the part (required). 83 | - `mode`: is `complete`, `interruptable` or `forever`. (optional) 84 | - `complete` means the part must be played completely, even if somebody requested EasySplash to 85 | stop (default). 86 | - `interruptable` means it can be stopped immediately. 87 | - `forever` means it will be looping forever and can be interrupted. 88 | - `repeat`: defines how many times a part is replayed before moving to next. (optional) 89 | 90 | 91 | License 92 | ------- 93 | 94 | EasySplash is licensed under either of 95 | 96 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 97 | http://www.apache.org/licenses/LICENSE-2.0) 98 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 99 | http://opensource.org/licenses/MIT) 100 | 101 | at your option. 102 | 103 | 104 | 105 | 106 | Contribution 107 | ------------ 108 | 109 | Any kinds of contributions are welcome as a pull request. 110 | 111 | Unless you explicitly state otherwise, any contribution intentionally submitted 112 | for inclusion in EasySplash by you, as defined in the Apache-2.0 license, shall 113 | be dual licensed as above, without any additional terms or conditions. 114 | -------------------------------------------------------------------------------- /data/glowing-logo/animation.toml: -------------------------------------------------------------------------------- 1 | [[part]] 2 | file = "part1.mp4" 3 | 4 | [[part]] 5 | file = "part2.mp4" 6 | 7 | [[part]] 8 | file = "part2.mp4" 9 | mode = "forever" 10 | -------------------------------------------------------------------------------- /data/glowing-logo/part1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSSystems/EasySplash/HEAD/data/glowing-logo/part1.mp4 -------------------------------------------------------------------------------- /data/glowing-logo/part2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSSystems/EasySplash/HEAD/data/glowing-logo/part2.mp4 -------------------------------------------------------------------------------- /data/ossystems-demo/animation.toml: -------------------------------------------------------------------------------- 1 | [[part]] 2 | file = "beginning.mp4" 3 | 4 | [[part]] 5 | file = "intermediate.mp4" 6 | 7 | [[part]] 8 | file = "end.mp4" 9 | -------------------------------------------------------------------------------- /data/ossystems-demo/beginning.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSSystems/EasySplash/HEAD/data/ossystems-demo/beginning.mp4 -------------------------------------------------------------------------------- /data/ossystems-demo/end.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSSystems/EasySplash/HEAD/data/ossystems-demo/end.mp4 -------------------------------------------------------------------------------- /data/ossystems-demo/intermediate.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSSystems/EasySplash/HEAD/data/ossystems-demo/intermediate.mp4 -------------------------------------------------------------------------------- /doc/demo-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OSSystems/EasySplash/HEAD/doc/demo-animation.gif -------------------------------------------------------------------------------- /etc/easysplash-quit.service.in: -------------------------------------------------------------------------------- 1 | # EasySplash - tool for animated splash screens 2 | # Copyright (C) 2014, 2015, 2020 O.S. Systems Software LTDA. 3 | # 4 | # This file is part of EasySplash. 5 | # 6 | # SPDX-License-Identifier: Apache-2.0 OR MIT 7 | 8 | [Unit] 9 | Description=Terminate EasySplash Boot Screen 10 | After=easysplash-start.service 11 | DefaultDependencies=no 12 | 13 | [Service] 14 | EnvironmentFile=-@SYSCONFDIR@/default/easysplash 15 | Type=oneshot 16 | ExecStart=@SBINDIR@/easysplash client --stop $EASYSPLASH_EXTRA_ARGS 17 | RemainAfterExit=yes 18 | TimeoutSec=30 19 | 20 | [Install] 21 | WantedBy=sysinit.target 22 | -------------------------------------------------------------------------------- /etc/easysplash-start.init.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # EasySplash - tool for animated splash screens 3 | # Copyright (C) 2015 O.S. Systems Software LTDA. 4 | # 5 | # This file is part of EasySplash. 6 | # 7 | # SPDX-License-Identifier: Apache-2.0 OR MIT 8 | 9 | ### BEGIN INIT INFO 10 | # Provides: easysplash 11 | # Required-Start: 12 | # Required-Stop: 13 | # Default-Start: S 14 | # Default-Stop: 15 | ### END INIT INFO 16 | 17 | # Read configuration variable file if it is present 18 | [ -r @SYSCONFDIR@/default/easysplash ] && . @SYSCONFDIR@/default/easysplash 19 | 20 | read CMDLINE < /proc/cmdline 21 | for x in $CMDLINE; do 22 | case $x in 23 | easysplash=false) 24 | echo "Boot splashscreen disabled" 25 | exit 0 26 | ;; 27 | esac 28 | done 29 | 30 | @SBINDIR@/easysplash open /lib/easysplash/oem/animation /lib/easysplash/animation ${EASYSPLASH_EXTRA_ARGS} & 31 | -------------------------------------------------------------------------------- /etc/easysplash-start.service.in: -------------------------------------------------------------------------------- 1 | # EasySplash - tool for animated splash screens 2 | # Copyright (C) 2014, 2015, 2020 O.S. Systems Software LTDA. 3 | # 4 | # This file is part of EasySplash. 5 | # 6 | # SPDX-License-Identifier: Apache-2.0 OR MIT 7 | 8 | [Unit] 9 | Description=Start EasySplash Boot screen 10 | Wants=systemd-vconsole-setup.service 11 | After=systemd-vconsole-setup.service systemd-udev-trigger.service systemd-udevd.service 12 | DefaultDependencies=no 13 | ConditionKernelCommandLine=!easysplash.enable=0 14 | ConditionVirtualization=!container 15 | IgnoreOnIsolate=true 16 | 17 | [Service] 18 | EnvironmentFile=-@SYSCONFDIR@/default/easysplash 19 | Type=notify 20 | ExecStart=@SBINDIR@/easysplash open /lib/easysplash/oem/animation /lib/easysplash/animation $EASYSPLASH_EXTRA_ARGS 21 | RemainAfterExit=yes 22 | KillMode=mixed 23 | 24 | [Install] 25 | WantedBy=sysinit.target 26 | -------------------------------------------------------------------------------- /etc/easysplash.default: -------------------------------------------------------------------------------- 1 | # EasySplash - tool for animated splash screens 2 | # Copyright (C) 2020 O.S. Systems Software LTDA. 3 | # 4 | # This file is part of EasySplash. 5 | # 6 | # SPDX-License-Identifier: Apache-2.0 OR MIT 7 | 8 | EASYSPLASH_EXTRA_ARGS="--runtime-dir /run/easysplash --log info" 9 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1681932375, 6 | "narHash": "sha256-tSXbYmpnKSSWpzOrs27ie8X3I0yqKA6AuCzCYNtwbCU=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "3d302c67ab8647327dba84fbdb443cdbf0e82744", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "id": "nixpkgs", 14 | "ref": "nixos-22.11", 15 | "type": "indirect" 16 | } 17 | }, 18 | "nixpkgs_2": { 19 | "locked": { 20 | "lastModified": 1681920287, 21 | "narHash": "sha256-+/d6XQQfhhXVfqfLROJoqj3TuG38CAeoT6jO1g9r1k0=", 22 | "owner": "nixos", 23 | "repo": "nixpkgs", 24 | "rev": "645bc49f34fa8eff95479f0345ff57e55b53437e", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "nixos", 29 | "ref": "nixos-unstable", 30 | "repo": "nixpkgs", 31 | "type": "github" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "nixpkgs": "nixpkgs", 37 | "rust": "rust" 38 | } 39 | }, 40 | "rust": { 41 | "inputs": { 42 | "nixpkgs": "nixpkgs_2", 43 | "rust-analyzer-src": "rust-analyzer-src" 44 | }, 45 | "locked": { 46 | "lastModified": 1682058140, 47 | "narHash": "sha256-vFevV/NfTnz5Qyo25gbM4LZgirzyAJOcxSlcJ6mT7m0=", 48 | "owner": "nix-community", 49 | "repo": "fenix", 50 | "rev": "53c3f9853aeaeb5a5ecba82527399fd0200feed0", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "nix-community", 55 | "repo": "fenix", 56 | "type": "github" 57 | } 58 | }, 59 | "rust-analyzer-src": { 60 | "flake": false, 61 | "locked": { 62 | "lastModified": 1682029577, 63 | "narHash": "sha256-EKAeXLtTNu3Ydgda6WF44Cx9S7gsF1QWehWTTRhIiLA=", 64 | "owner": "rust-lang", 65 | "repo": "rust-analyzer", 66 | "rev": "0289dfa2611a605975e10dab6d3931c21509f90c", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "rust-lang", 71 | "ref": "nightly", 72 | "repo": "rust-analyzer", 73 | "type": "github" 74 | } 75 | } 76 | }, 77 | "root": "root", 78 | "version": 7 79 | } 80 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "EasySplash"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-22.11"; 6 | rust.url = "github:nix-community/fenix"; 7 | }; 8 | 9 | outputs = { nixpkgs, rust, ... }: 10 | let 11 | forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed; 12 | in 13 | { 14 | devShell = forAllSystems 15 | (system: 16 | let 17 | pkgs = nixpkgs.legacyPackages.${system}; 18 | 19 | rust-toolchain = with rust.packages.${system}; 20 | let 21 | msrvToolchain = toolchainOf { 22 | channel = "1.66.0"; 23 | sha256 = "sha256-S7epLlflwt0d1GZP44u5Xosgf6dRrmr8xxC+Ml2Pq7c="; 24 | }; 25 | in 26 | combine [ 27 | (msrvToolchain.withComponents [ "rustc" "cargo" "rust-src" "clippy" ]) 28 | (latest.withComponents [ "rustfmt" "rust-analyzer" ]) 29 | ]; 30 | in 31 | pkgs.mkShell { 32 | buildInputs = with pkgs; [ 33 | glib 34 | gst_all_1.gst-libav 35 | gst_all_1.gst-plugins-base 36 | gst_all_1.gst-plugins-good 37 | gst_all_1.gstreamer 38 | pkg-config 39 | rust-toolchain 40 | systemd 41 | 42 | cargo-release 43 | cargo-outdated 44 | cargo-edit 45 | ]; 46 | } 47 | ); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | version = "Two" 2 | use_small_heuristics = "Max" 3 | edition = "2018" 4 | reorder_imports = true 5 | condense_wildcard_suffixes = true 6 | merge_imports = true 7 | reorder_impl_items = true 8 | use_field_init_shorthand = true 9 | use_try_shorthand = true 10 | wrap_comments = true 11 | -------------------------------------------------------------------------------- /src/animation.rs: -------------------------------------------------------------------------------- 1 | // EasySplash - tool for animated splash screens 2 | // Copyright (C) 2020 O.S. Systems Software LTDA. 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 OR MIT 5 | 6 | use derive_more::{Display, Error, From}; 7 | use log::{debug, error, info, trace}; 8 | use serde::Deserialize; 9 | use std::{ 10 | fs, io, 11 | io::Read, 12 | iter::Iterator, 13 | path::{Path, PathBuf}, 14 | }; 15 | 16 | #[derive(Display, From, Error, Debug)] 17 | pub(crate) enum Error { 18 | #[display(fmt = "Fail to read the manifest file. Cause: {}", _0)] 19 | Io(io::Error), 20 | 21 | #[display(fmt = "Failed to parse the manifest file. Cause: {}", _0)] 22 | TomlParser(toml::de::Error), 23 | 24 | #[display(fmt = "The animation part '{}' is missing.", "_0.display()")] 25 | MissingPart(#[error(not(source))] PathBuf), 26 | 27 | #[display(fmt = "The part with mode set as 'forever' must be the last one")] 28 | WrongModeForeverPart, 29 | } 30 | 31 | #[derive(Debug, Deserialize, PartialEq)] 32 | #[serde(deny_unknown_fields)] 33 | pub(crate) struct Animation { 34 | #[serde(rename = "part")] 35 | parts: Vec, 36 | } 37 | 38 | #[derive(Debug, Deserialize, PartialEq)] 39 | #[serde(deny_unknown_fields)] 40 | pub(crate) struct Part { 41 | file: PathBuf, 42 | #[serde(default)] 43 | mode: Mode, 44 | #[serde(default)] 45 | repeat: usize, 46 | } 47 | 48 | impl Animation { 49 | pub(crate) fn from_path(path: &Path) -> Result { 50 | info!("loading manifest from {:?}", path); 51 | 52 | let mut buf = String::new(); 53 | fs::File::open(path.join("animation.toml"))?.read_to_string(&mut buf)?; 54 | 55 | let mut animation = toml::from_str::(&buf)?; 56 | 57 | // We ensure we have the full path to the animation files, while we also 58 | // ensure all files exists. 59 | for part in &mut animation.parts { 60 | part.file = fs::canonicalize(path)?.join(&part.file); 61 | if !part.file.exists() { 62 | error!("part {:?} is missing", part.file); 63 | return Err(Error::MissingPart(part.file.to_path_buf())); 64 | } 65 | trace!("part {:?} was found", part.file); 66 | } 67 | 68 | animation.validate_modes()?; 69 | 70 | Ok(animation) 71 | } 72 | 73 | fn validate_modes(&self) -> Result<(), Error> { 74 | // Ensure if there is any part with `Mode::Forever` it is the last one, 75 | // or it will never be played. 76 | let mut iter = self.parts.iter(); 77 | if iter.any(|p| p.mode == Mode::Forever) && iter.next().is_some() { 78 | return Err(Error::WrongModeForeverPart); 79 | } 80 | 81 | Ok(()) 82 | } 83 | } 84 | 85 | pub(crate) struct AnimationIter<'a> { 86 | inner: &'a Animation, 87 | current_part: usize, 88 | repeat: usize, 89 | } 90 | 91 | impl<'a> IntoIterator for &'a Animation { 92 | type IntoIter = AnimationIter<'a>; 93 | type Item = &'a Part; 94 | 95 | fn into_iter(self) -> Self::IntoIter { 96 | AnimationIter { inner: self, current_part: 0, repeat: 0 } 97 | } 98 | } 99 | 100 | // Provides an iterator which respects the number of times each part must be 101 | // played. 102 | impl<'a> Iterator for AnimationIter<'a> { 103 | type Item = &'a Part; 104 | 105 | fn next(&mut self) -> Option { 106 | let part = self.inner.parts.get(self.current_part)?; 107 | 108 | // In case of `Mode::Forever` we just return the part. 109 | if let Mode::Forever = part.mode { 110 | return Some(part); 111 | } 112 | 113 | // Otherwise, when we iterate the number of times which are required by 114 | // the `current_part`, we move to the next. 115 | let repeat = part.repeat; 116 | if self.repeat > repeat { 117 | self.current_part += 1; 118 | self.repeat = 0; 119 | } 120 | 121 | // Get the required part for returning it. 122 | let part = self.inner.parts.get(self.current_part)?; 123 | 124 | if repeat > 0 { 125 | debug!( 126 | "iterator: part {:?} (current: {} / number of times: {})", 127 | part.file, 128 | self.repeat + 1, 129 | repeat + 1 130 | ); 131 | } else { 132 | debug!("iterator: part {:?} (once)", part.file); 133 | } 134 | 135 | // Account for the number of repetitions we need to do. 136 | self.repeat += 1; 137 | 138 | Some(part) 139 | } 140 | } 141 | 142 | impl Part { 143 | pub(crate) fn url(&self) -> String { 144 | format!("file://{}", self.file.to_string_lossy()) 145 | } 146 | 147 | pub(crate) fn is_interruptable(&self) -> bool { 148 | self.mode == Mode::Interruptable || self.mode == Mode::Forever 149 | } 150 | } 151 | 152 | #[derive(Debug, Deserialize, PartialEq, Clone)] 153 | #[serde(rename_all = "snake_case")] 154 | pub(crate) enum Mode { 155 | Complete, 156 | Interruptable, 157 | Forever, 158 | } 159 | 160 | impl Default for Mode { 161 | fn default() -> Self { 162 | Mode::Complete 163 | } 164 | } 165 | 166 | #[cfg(test)] 167 | mod test { 168 | use super::*; 169 | 170 | fn valid_toml() -> Animation { 171 | let manifest_toml = toml::toml! { 172 | [[part]] 173 | file = "part1.mp4" 174 | 175 | [[part]] 176 | file = "part2.mp4" 177 | repeat = 1 178 | 179 | [[part]] 180 | file = "part3.mp4" 181 | mode = "interruptable" 182 | }; 183 | 184 | manifest_toml.try_into::().expect("Failed to parse TOML") 185 | } 186 | 187 | #[test] 188 | fn forever_mode_must_be_last_part() { 189 | let animation = toml::toml! { 190 | [[part]] 191 | file = "part1.mp4" 192 | mode = "forever" 193 | 194 | [[part]] 195 | file = "part2.mp4" 196 | } 197 | .try_into::() 198 | .expect("Failed to parse TOML"); 199 | 200 | animation.validate_modes().unwrap_err(); 201 | } 202 | 203 | #[test] 204 | fn manifest_parse() { 205 | assert_eq!( 206 | valid_toml(), 207 | Animation { 208 | parts: vec![ 209 | Part { file: "part1.mp4".into(), mode: Mode::Complete, repeat: 0 }, 210 | Part { file: "part2.mp4".into(), mode: Mode::Complete, repeat: 1 }, 211 | Part { file: "part3.mp4".into(), mode: Mode::Interruptable, repeat: 0 }, 212 | ] 213 | } 214 | ); 215 | } 216 | 217 | #[test] 218 | fn iterator_takes_repeat_under_consideration_when_returning() { 219 | let animation = valid_toml(); 220 | let mut animation: AnimationIter = animation.into_iter(); 221 | 222 | assert_eq!( 223 | animation.next(), 224 | Some(&Part { file: "part1.mp4".into(), mode: Mode::Complete, repeat: 0 }) 225 | ); 226 | 227 | assert_eq!( 228 | animation.next(), 229 | Some(&Part { file: "part2.mp4".into(), mode: Mode::Complete, repeat: 1 }) 230 | ); 231 | 232 | assert_eq!( 233 | animation.next(), 234 | Some(&Part { file: "part2.mp4".into(), mode: Mode::Complete, repeat: 1 }) 235 | ); 236 | 237 | assert_eq!( 238 | animation.next(), 239 | Some(&Part { file: "part3.mp4".into(), mode: Mode::Interruptable, repeat: 0 }) 240 | ); 241 | } 242 | 243 | #[test] 244 | fn part_with_mode_forever_keeps_returning_on_iter() { 245 | let animation = toml::toml! { 246 | [[part]] 247 | file = "part1.mp4" 248 | 249 | [[part]] 250 | file = "part2.mp4" 251 | mode = "forever" 252 | } 253 | .try_into::() 254 | .expect("Failed to parse TOML"); 255 | 256 | let mut animation: AnimationIter = animation.into_iter(); 257 | 258 | assert_eq!( 259 | animation.next(), 260 | Some(&Part { file: "part1.mp4".into(), mode: Mode::Complete, repeat: 0 }) 261 | ); 262 | 263 | assert_eq!( 264 | animation.next(), 265 | Some(&Part { file: "part2.mp4".into(), mode: Mode::Forever, repeat: 0 }) 266 | ); 267 | 268 | assert_eq!( 269 | animation.next(), 270 | Some(&Part { file: "part2.mp4".into(), mode: Mode::Forever, repeat: 0 }) 271 | ); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/gstreamer.rs: -------------------------------------------------------------------------------- 1 | // EasySplash - tool for animated splash screens 2 | // Copyright (C) 2020 O.S. Systems Software LTDA. 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 OR MIT 5 | 6 | use crate::{animation::Animation, message::Message}; 7 | use async_std::{ 8 | channel, io, 9 | os::unix::net::UnixListener, 10 | prelude::*, 11 | sync::{Arc, Mutex}, 12 | task, 13 | }; 14 | use derive_more::{Display, Error, From}; 15 | use gst::{prelude::*, MessageView}; 16 | use log::{debug, error, trace}; 17 | 18 | #[derive(Display, From, Error, Debug)] 19 | pub(crate) enum Error { 20 | #[display(fmt = "No animation parts to play")] 21 | NoAnimation, 22 | 23 | #[display(fmt = "Input/Output error: {}", _0)] 24 | Io(io::Error), 25 | 26 | #[display(fmt = "GLib error: {}", _0)] 27 | Bool(gst::glib::error::BoolError), 28 | 29 | #[display(fmt = "GLib error: {}", _0)] 30 | Glib(gst::glib::error::Error), 31 | 32 | #[display(fmt = "GStreamer error while changing state: {}", _0)] 33 | StateChange(gst::StateChangeError), 34 | 35 | #[display(fmt = "Error reading data in channel")] 36 | ChannelReceive(channel::RecvError), 37 | 38 | #[display(fmt = "Error sending data in channel")] 39 | ChannelSend, 40 | } 41 | 42 | impl From> for Error { 43 | fn from(_e: channel::SendError) -> Self { 44 | // The original error from async_std::channel::Sender carries the undelivered 45 | // message for recovery. However here we want to avoid raising the arity of 46 | // the Error type, losing that ability but making the error type more 47 | // permissive 48 | Error::ChannelSend 49 | } 50 | } 51 | 52 | enum PipelineStatus { 53 | Continuous, 54 | Interruptable, 55 | } 56 | 57 | pub(crate) async fn play_animation( 58 | animation: Animation, 59 | video_sink: Option, 60 | socket: UnixListener, 61 | ) -> Result<(), Error> { 62 | gst::init()?; 63 | debug!("Using {} as player", gst::version_string()); 64 | 65 | let (status_tx, status_rx) = channel::bounded(1); 66 | let (message_tx, message_rx) = channel::bounded(1); 67 | 68 | let playbin = gst::ElementFactory::make("playbin").build()?; 69 | 70 | // Use a custom 'video-sink' if required. By default, we use the 'playbin' 71 | // one. 72 | if let Some(video_sink) = video_sink { 73 | playbin.set_property("video-sink", gst::parse_bin_from_description(&video_sink, true)?); 74 | }; 75 | 76 | // The pipeline is feed by the `feed_pipeline` and the control messages are 77 | // handled by the `handle_message` future. 78 | // 79 | // Any future which finishes, allow the flow to continue. 80 | feed_pipeline(status_tx, playbin.clone(), animation) 81 | .race(handle_client_message(message_tx, socket)) 82 | .race(handle_interrupt_message(status_rx, message_rx)) 83 | .await?; 84 | 85 | playbin.set_state(gst::State::Null)?; 86 | 87 | Ok(()) 88 | } 89 | 90 | async fn feed_pipeline( 91 | tx: channel::Sender, 92 | playbin: gst::Element, 93 | animation: Animation, 94 | ) -> Result<(), Error> { 95 | // Acquire the iterator so we can walk on the animation parts. 96 | let mut parts = animation.into_iter(); 97 | 98 | // Current playing part. 99 | let mut current_part = parts.next().ok_or(Error::NoAnimation)?; 100 | 101 | // Queue first animation part and ask GStreamer to start playing it. 102 | playbin.set_property("uri", ¤t_part.url()); 103 | playbin.set_state(gst::State::Playing)?; 104 | 105 | // We need to wait for stream to start and then we can queue the next 106 | // part. We do that so we have a gapless playback. 107 | let bus = playbin.bus().expect("failed to get pipeline bus"); 108 | let mut messages = bus.stream(); 109 | while let Some(msg) = messages.next().await { 110 | match msg.view() { 111 | MessageView::Error(err) => { 112 | error!("{}", err.error()); 113 | break; 114 | } 115 | MessageView::Eos(_) => { 116 | trace!("end of stream message recived, finishing"); 117 | break; 118 | } 119 | MessageView::StreamStart(_) => { 120 | // Notify if current part is interruptable. 121 | let status = if current_part.is_interruptable() { 122 | debug!("animation part is interruptable"); 123 | PipelineStatus::Interruptable 124 | } else { 125 | debug!("animation part is intended to be played completely"); 126 | PipelineStatus::Continuous 127 | }; 128 | 129 | tx.send(status).await?; 130 | 131 | // If we have more animation parts to play, queue the next. 132 | if let Some(part) = parts.next() { 133 | current_part = part; 134 | 135 | trace!("video has started, queuing next part"); 136 | playbin.set_property("uri", ¤t_part.url()); 137 | } 138 | } 139 | _ => (), 140 | }; 141 | } 142 | 143 | Ok(()) 144 | } 145 | 146 | async fn handle_client_message( 147 | tx: channel::Sender, 148 | socket: UnixListener, 149 | ) -> Result<(), Error> { 150 | while let Some(stream) = socket.incoming().next().await { 151 | tx.send(Message::from(stream?.bytes().next().await.expect("unexpected EOF")?)).await? 152 | } 153 | 154 | Ok(()) 155 | } 156 | 157 | async fn handle_interrupt_message( 158 | status_rx: channel::Receiver, 159 | message_rx: channel::Receiver, 160 | ) -> Result<(), Error> { 161 | let interruptable = Arc::new(Mutex::new(false)); 162 | 163 | // This future is responsible to monitor the status changes for the pipeline 164 | // and mark if it is interruptable or not. 165 | let status_fut = { 166 | let interruptable = interruptable.clone(); 167 | async move { 168 | loop { 169 | match status_rx.recv().await? { 170 | PipelineStatus::Continuous => *interruptable.lock().await = false, 171 | PipelineStatus::Interruptable => *interruptable.lock().await = true, 172 | } 173 | } 174 | } 175 | }; 176 | 177 | // The client messages are handled in this future and it takes the 178 | // interruptable status in consideration. 179 | let message_fut = async move { 180 | 'outter: loop { 181 | match message_rx.recv().await? { 182 | Message::Interrupt => loop { 183 | if *interruptable.lock().await { 184 | break 'outter Ok(()); 185 | } 186 | 187 | // The yield is required to avoid the status_fut to starve. 188 | task::yield_now().await; 189 | }, 190 | } 191 | } 192 | }; 193 | 194 | status_fut.race(message_fut).await 195 | } 196 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // EasySplash - tool for animated splash screens 2 | // Copyright (C) 2020 O.S. Systems Software LTDA. 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 OR MIT 5 | 6 | mod animation; 7 | mod gstreamer; 8 | mod message; 9 | 10 | use crate::{animation::Animation, message::Message}; 11 | 12 | use argh::FromArgs; 13 | use log::{info, LevelFilter}; 14 | use std::path::PathBuf; 15 | 16 | /// EasySplash offers a convenient boot splash for Embedded Linux devices, 17 | /// focusing of simplicity and easy to use. 18 | #[derive(FromArgs)] 19 | struct CmdLine { 20 | #[argh(subcommand)] 21 | inner: Commands, 22 | } 23 | 24 | #[derive(FromArgs)] 25 | #[argh(subcommand)] 26 | enum Commands { 27 | Open(Open), 28 | Client(Client), 29 | } 30 | 31 | /// open the render with the specific animation 32 | #[derive(FromArgs)] 33 | #[argh(subcommand, name = "open")] 34 | struct Open { 35 | /// paths to try to load the animation 36 | #[argh(positional)] 37 | paths: Vec, 38 | 39 | /// runtime directory (default to '/tmp/easysplash') 40 | #[argh(option, default = "PathBuf::from(\"/tmp/easysplash\")")] 41 | runtime_dir: PathBuf, 42 | 43 | /// custom video-sink to use when playing the animation 44 | #[argh(option)] 45 | video_sink: Option, 46 | 47 | /// log level to use (default to 'info') 48 | #[argh(option, default = "LevelFilter::Info")] 49 | log: LevelFilter, 50 | } 51 | 52 | /// control the render from the user space 53 | #[derive(FromArgs)] 54 | #[argh(subcommand, name = "client")] 55 | struct Client { 56 | /// stop the render as soon as possible 57 | #[argh(switch)] 58 | stop: bool, 59 | 60 | /// runtime directory (default to '/tmp/easysplash') 61 | #[argh(option, default = "PathBuf::from(\"/tmp/easysplash\")")] 62 | runtime_dir: PathBuf, 63 | 64 | /// log level to use (default to info) 65 | #[argh(option, default = "LevelFilter::Info")] 66 | log: LevelFilter, 67 | } 68 | 69 | async fn open(args: Open) -> Result<(), anyhow::Error> { 70 | simple_logging::log_to_stderr(args.log); 71 | 72 | #[cfg(feature = "systemd")] 73 | systemd::daemon::notify(false, [(systemd::daemon::STATE_READY, "1")].iter())?; 74 | 75 | info!("starting EasySplash animation"); 76 | 77 | match args.paths.iter().find(|p| p.exists()) { 78 | Some(path) => { 79 | let socket = message::bind_socket(args.runtime_dir).await?; 80 | gstreamer::play_animation(Animation::from_path(path)?, args.video_sink, socket).await?; 81 | 82 | Ok(()) 83 | } 84 | None => Err(anyhow::format_err!("could not load any of provided animations")), 85 | } 86 | } 87 | 88 | async fn client(args: Client) -> Result<(), anyhow::Error> { 89 | simple_logging::log_to_stderr(args.log); 90 | 91 | if args.stop { 92 | message::send(args.runtime_dir, Message::Interrupt).await?; 93 | } 94 | 95 | Ok(()) 96 | } 97 | 98 | #[async_std::main] 99 | async fn main() -> Result<(), anyhow::Error> { 100 | let cmdline: CmdLine = argh::from_env(); 101 | 102 | match cmdline.inner { 103 | Commands::Open(args) => open(args).await?, 104 | Commands::Client(args) => client(args).await?, 105 | } 106 | 107 | Ok(()) 108 | } 109 | -------------------------------------------------------------------------------- /src/message.rs: -------------------------------------------------------------------------------- 1 | // EasySplash - tool for animated splash screens 2 | // Copyright (C) 2020 O.S. Systems Software LTDA. 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 OR MIT 5 | 6 | use async_std::{ 7 | io, 8 | os::unix::net::{UnixListener, UnixStream}, 9 | prelude::*, 10 | }; 11 | use log::trace; 12 | use std::{fs, path::PathBuf}; 13 | 14 | #[derive(Debug)] 15 | pub(crate) enum Message { 16 | /// Send a interrupt request for the animation. 17 | Interrupt, 18 | } 19 | 20 | pub(crate) async fn bind_socket(runtime_dir: PathBuf) -> Result { 21 | let path = unix_socket_file(runtime_dir, true).await?; 22 | if path.exists() { 23 | fs::remove_file(&path)?; 24 | } 25 | 26 | trace!("Binding to {:?} unix socket", &path); 27 | UnixListener::bind(&path).await 28 | } 29 | 30 | pub(crate) async fn send(runtime_dir: PathBuf, msg: Message) -> Result<(), io::Error> { 31 | trace!("Sending stop request"); 32 | UnixStream::connect(unix_socket_file(runtime_dir, false).await?) 33 | .await? 34 | .write_all(&[msg.into()]) 35 | .await 36 | } 37 | 38 | impl From for Message { 39 | fn from(v: u8) -> Self { 40 | match v { 41 | 0x1 => Message::Interrupt, 42 | _ => unreachable!("invalid message code"), 43 | } 44 | } 45 | } 46 | 47 | impl From for u8 { 48 | fn from(msg: Message) -> Self { 49 | match msg { 50 | Message::Interrupt => 0x1, 51 | } 52 | } 53 | } 54 | 55 | async fn unix_socket_file( 56 | runtime_dir: PathBuf, 57 | create_missing: bool, 58 | ) -> Result { 59 | if create_missing && !runtime_dir.exists() { 60 | fs::create_dir_all(&runtime_dir)?; 61 | } 62 | 63 | Ok(runtime_dir.join("ipc.socket")) 64 | } 65 | --------------------------------------------------------------------------------