diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf9089df..d261324d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,12 +13,12 @@ jobs: runs-on: ubuntu-latest container: docker://hhmhh/weylus_build:latest steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: deps/dist* key: ${{ runner.os }}-deps-${{ hashFiles('deps/*') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: | ~/.cargo/registry @@ -29,22 +29,22 @@ jobs: run: ./docker_build.sh shell: bash - name: Artifacts1 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: linux path: packages/weylus-linux.zip - name: Artifacts2 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: linux-deb path: packages/Weylus*.deb - name: Artifacts3 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: windows path: packages/weylus-windows.zip - name: Publish - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | @@ -59,12 +59,12 @@ jobs: runs-on: ubuntu-latest container: docker://hhmhh/weylus_build_alpine:latest steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: deps/dist* key: ${{ runner.os }}-alpine-deps-${{ hashFiles('deps/*') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: | ~/.cargo/registry @@ -72,15 +72,15 @@ jobs: target key: ${{ runner.os }}-alpine-cargo-${{ hashFiles('Cargo.lock') }} - name: Build - run: cargo build --release && cd target/release && tar czf weylus-linux-alpine-musl.tar.gz weylus + run: RUSTFLAGS='-C target-feature=-crt-static' cargo build --release && cd target/release && tar czf weylus-linux-alpine-musl.tar.gz weylus shell: bash - name: Artifacts1 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: linux-alpine-musl path: target/release/weylus-linux-alpine-musl.tar.gz - name: Publish - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | @@ -92,12 +92,12 @@ jobs: build-macos: runs-on: macOS-latest steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: deps/dist key: ${{ runner.os }}-deps-${{ hashFiles('deps/*') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: | ~/.cargo/registry @@ -116,14 +116,14 @@ jobs: - name: Package run: cd target/release/bundle/osx/ && zip -r macOS.zip Weylus.app - name: Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ runner.os }} path: | target/release/bundle/osx/macOS.zip - name: ArtifactsDebug if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ runner.os }}-ffbuild path: | @@ -136,7 +136,7 @@ jobs: SSH_PASS: ${{ secrets.SSH_PASS }} NGROK_REGION: eu - name: Publish - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | diff --git a/.gitignore b/.gitignore index c25cbb59..5cf1276a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ c_helper/target c_helper/Cargo.lock *.js *.js.map +*.tar.gz diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1558fa84..707086fb 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -7,6 +7,8 @@ List of Contributors: ************************** Robert Schroll Daniel Rutz +Philipp Urlbauer +OmegaRogue ************************** 3-Clause BSD License diff --git a/Cargo.lock b/Cargo.lock index 8b1cbc05..90adfd5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "adler32" version = "1.2.0" @@ -9,45 +30,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] -name = "ansi_term" -version = "0.12.1" +name = "anstream" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ - "winapi", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "anyhow" -version = "1.0.59" +name = "anstyle" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91f1f46651137be86f3a2b9a8359f9ab421d04d941c62b5982e1ca21113adf9" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle-parse" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "utf8parse", ] [[package]] -name = "autocfg" -version = "0.1.8" +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ - "autocfg 1.1.0", + "anstyle", + "windows-sys 0.52.0", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atomic_refcell" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" + [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "autopilot" @@ -67,23 +114,25 @@ dependencies = [ ] [[package]] -name = "base64" -version = "0.9.3" +name = "backtrace" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ - "byteorder", - "safemem", + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.8.0", + "object", + "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] name = "base64" -version = "0.10.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -92,78 +141,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "block" -version = "0.1.6" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] -name = "block-buffer" -version = "0.7.3" +name = "block" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] -name = "block-padding" -version = "0.1.5" +name = "bytemuck" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] -name = "byte-tools" -version = "0.3.1" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "bytemuck" -version = "1.11.0" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] -name = "byteorder" -version = "1.4.3" +name = "bytes" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] -name = "bytes" -version = "1.2.1" +name = "captrs" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dad312f1be0ea3cce9ad0cdbace33350ea0781f138d7b49bfe191df0aa83db1b" +dependencies = [ + "dxgcap", + "x11cap", +] [[package]] name = "cc" -version = "1.0.73" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "shlex", +] [[package]] name = "cfg-expr" -version = "0.10.3" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c" dependencies = [ "smallvec", + "target-lexicon", ] [[package]] @@ -173,39 +224,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "checked_int_cast" -version = "1.0.0" +name = "clap" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +dependencies = [ + "clap_builder", + "clap_derive", +] [[package]] -name = "clap" -version = "2.34.0" +name = "clap_builder" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ - "ansi_term", - "atty", - "bitflags", + "anstream", + "anstyle", + "clap_lex", "strsim", - "textwrap", - "unicode-width", ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "clap_complete" +version = "4.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "bitflags", + "heck", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cmake" -version = "0.1.48" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" dependencies = [ "cc", ] @@ -216,11 +287,11 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation 0.7.0", "core-graphics 0.19.2", - "foreign-types", + "foreign-types 0.3.2", "libc", "objc", ] @@ -231,6 +302,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "core-foundation" version = "0.7.0" @@ -243,11 +320,11 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ - "core-foundation-sys 0.8.3", + "core-foundation-sys 0.8.7", "libc", ] @@ -259,9 +336,9 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" @@ -269,99 +346,87 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation 0.7.0", - "foreign-types", + "foreign-types 0.3.2", "libc", ] [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags", - "core-foundation 0.9.3", + "bitflags 2.6.0", + "core-foundation 0.10.0", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags", - "core-foundation 0.9.3", - "foreign-types", + "bitflags 2.6.0", + "core-foundation 0.10.0", "libc", ] [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg 1.1.0", - "cfg-if", "crossbeam-utils", - "memoffset", - "once_cell", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" -dependencies = [ - "cfg-if", - "once_cell", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -369,15 +434,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array", "typenum", ] [[package]] name = "dbus" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" dependencies = [ "libc", "libdbus-sys", @@ -395,89 +460,129 @@ dependencies = [ ] [[package]] -name = "deflate" -version = "0.8.6" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "adler32", - "byteorder", + "block-buffer", + "crypto-common", ] [[package]] -name = "digest" -version = "0.8.1" +name = "dirs" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "generic-array 0.12.4", + "dirs-sys", ] [[package]] -name = "digest" -version = "0.10.3" +name = "dirs-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ - "block-buffer 0.10.2", - "crypto-common", + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", ] [[package]] -name = "dirs" -version = "4.0.0" +name = "dxgcap" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "18a3f3df51c509e7c8d2807c63abfecdea1a83def81baec83d68c6f5bb8ac654" dependencies = [ - "dirs-sys", + "winapi", + "wio", ] [[package]] -name = "dirs-sys" -version = "0.3.7" +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fastwebsockets" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" dependencies = [ - "libc", - "redox_users", - "winapi", + "base64", + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "pin-project", + "rand 0.8.5", + "sha1", + "simdutf8", + "thiserror", + "tokio", + "utf-8", ] [[package]] -name = "either" -version = "1.7.0" +name = "fdeflate" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] [[package]] -name = "fake-simd" -version = "0.1.2" +name = "flate2" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] [[package]] name = "fltk" -version = "1.3.12" +version = "1.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23e9952a97c05f16980e9e4c8f1947a446e2f73f6dea9a1635284b4b865f184" +checksum = "f8f81f9ebf85aeaad76e43362cc2bb1d926e5daca1327dc0a094d00c86a95ebb" dependencies = [ - "bitflags", + "bitflags 2.6.0", "crossbeam-channel", "fltk-sys", - "lazy_static", + "once_cell", "paste", "ttf-parser", ] [[package]] name = "fltk-sys" -version = "1.3.12" +version = "1.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc98ac94df90651d5ca5082b2990ce817422e046078d5c3906a046823c52802a" +checksum = "30bbe32c3f9f72469e2cf2a62edc74ef990b81c2caf082f53fd67060ce0c9893" dependencies = [ "cmake", ] +[[package]] +name = "fltk-theme" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03302d43154c9c51b4297a96f40c85ea913bb15ea27b6a38665284ddae8ac3d7" +dependencies = [ + "fltk", +] + [[package]] name = "fnv" version = "1.0.7" @@ -490,76 +595,108 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", ] [[package]] -name = "foreign-types-shared" -version = "0.1.1" +name = "foreign-types" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] [[package]] -name = "form_urlencoded" -version = "1.0.1" +name = "foreign-types-macros" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "matches", - "percent-encoding 2.1.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] -name = "fuchsia-cprng" +name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", @@ -568,21 +705,12 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -598,9 +726,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -617,46 +745,64 @@ dependencies = [ "lzw", ] +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "gio-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5237611e97e9b86ab5768adc3eef853ae713ea797aa3835404acdfacffc9fb38" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.52.0", +] + [[package]] name = "glib" -version = "0.15.12" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "95648aac01b75503000bb3bcaa5ec7a7a2dd61e43636b8b1814854de94dd80e4" dependencies = [ - "bitflags", + "bitflags 2.6.0", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", - "once_cell", + "memchr", "smallvec", - "thiserror", ] [[package]] name = "glib-macros" -version = "0.15.11" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +checksum = "302f1d633c9cdef4350330e7b68fd8016e2834bb106c93fdf9789fcde753c1ab" dependencies = [ - "anyhow", - "heck 0.4.0", + "heck", "proc-macro-crate", - "proc-macro-error", - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "92eee4531c1c9abba945d19378b205031b5890e1f99c319ba0503b6e0c06a163" dependencies = [ "libc", "system-deps", @@ -664,9 +810,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "fa3d1dcd8a1eb2e7c22be3d5e792b14b186f3524f79b25631730f9a8c169d49a" dependencies = [ "glib-sys", "libc", @@ -675,35 +821,35 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.18.8" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66363bacf5e4f6eb281564adc2902e44c52ae5c45082423e7439e9012b75456" +checksum = "683e15f8cc3a1a2646d9fe2181a58b7abb4c166256d8d15cce368e420c741140" dependencies = [ - "bitflags", "cfg-if", "futures-channel", "futures-core", "futures-util", "glib", "gstreamer-sys", + "itertools", "libc", "muldiv", "num-integer", - "num-rational 0.4.1", + "num-rational 0.4.2", "once_cell", "option-operations", "paste", - "pretty-hex", + "pin-project-lite", + "smallvec", "thiserror", ] [[package]] name = "gstreamer-app" -version = "0.18.7" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664adf6abc6546c1ad54492a067dcbc605032c9c789ce8f6f78cb9ddeef4b684" +checksum = "e0c86915cc4cdfa030532301a46c725e0ce0c6c2b57a68c44ce9b34db587e552" dependencies = [ - "bitflags", "futures-core", "futures-sink", "glib", @@ -711,14 +857,13 @@ dependencies = [ "gstreamer-app-sys", "gstreamer-base", "libc", - "once_cell", ] [[package]] name = "gstreamer-app-sys" -version = "0.18.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b401f21d731b3e5de802487f25507fabd34de2dd007d582f440fb1c66a4fbb" +checksum = "37066c1b93ba57aa070ebc1e0a564bc1a9adda78fb0850e624861fad46fd1448" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -729,11 +874,11 @@ dependencies = [ [[package]] name = "gstreamer-base" -version = "0.18.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224f35f36582407caf58ded74854526beeecc23d0cf64b8d1c3e00584ed6863f" +checksum = "ed5d73cb5cbf229c8904fba5ff93b1863f186bccc062064c1b2a9000750cc06e" dependencies = [ - "bitflags", + "atomic_refcell", "cfg-if", "glib", "gstreamer", @@ -743,9 +888,9 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" -version = "0.18.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a083493c3c340e71fa7c66eebda016e9fafc03eb1b4804cf9b2bad61994b078e" +checksum = "4a6643ef963c636b8022adc93aa19eac6f356bd174a187c499339fc5d64c1e05" dependencies = [ "glib-sys", "gobject-sys", @@ -756,9 +901,9 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.18.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3517a65d3c2e6f8905b456eba5d53bda158d664863aef960b44f651cb7d33e2" +checksum = "d9c9005b55dd2b1784645963c1ec409f9d420a56f6348d0ae69c2eaff584bcc3" dependencies = [ "glib-sys", "gobject-sys", @@ -768,11 +913,10 @@ dependencies = [ [[package]] name = "gstreamer-video" -version = "0.18.7" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9418adfc72dafa1ad9eb106527ce4804887d101027c4528ec28c7d29cc899519" +checksum = "57332bca1ae7825a53fe57d993b63389f132d335aed691ac76f0ffe4304548e3" dependencies = [ - "bitflags", "cfg-if", "futures-channel", "glib", @@ -781,13 +925,14 @@ dependencies = [ "gstreamer-video-sys", "libc", "once_cell", + "thiserror", ] [[package]] name = "gstreamer-video-sys" -version = "0.18.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33331b1675e73b5b000c796354278eca7fdde9327015971d9f41afe28b96e0dc" +checksum = "0f5c334d143384e8dc714af948c2e5d7d12cb588fdcfb56f3bf37c24daf350ef" dependencies = [ "glib-sys", "gobject-sys", @@ -799,15 +944,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.13" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -818,11 +963,11 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.3" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360d9740069b2f6cbb63ce2dbaa71a20d3185350cbb990d7bebeb9318415eb17" +checksum = "ce25b617d1375ef96eeb920ae717e3da34a02fc979fe632c75128350f9e1f74a" dependencies = [ - "log 0.4.17", + "log", "pest", "pest_derive", "serde", @@ -832,39 +977,27 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.3.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "http" -version = "0.2.8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -873,55 +1006,47 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", - "pin-project-lite", ] [[package]] -name = "httparse" -version = "1.7.1" +name = "http-body-util" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] [[package]] -name = "httpdate" -version = "1.0.2" +name = "httparse" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] -name = "hyper" -version = "0.10.16" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -dependencies = [ - "base64 0.9.3", - "httparse", - "language-tags", - "log 0.3.9", - "mime", - "num_cpus", - "time", - "traitobject", - "typeable", - "unicase", - "url 1.7.2", -] +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.20" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", @@ -930,31 +1055,32 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] -name = "idna" -version = "0.1.5" +name = "hyper-util" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", ] [[package]] name = "idna" -version = "0.2.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -978,26 +1104,23 @@ dependencies = [ [[package]] name = "image" -version = "0.23.14" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" dependencies = [ "bytemuck", - "byteorder", - "color_quant", - "num-iter", - "num-rational 0.3.2", + "byteorder-lite", "num-traits", - "png 0.16.8", + "png 0.17.13", ] [[package]] name = "indexmap" -version = "1.9.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ - "autocfg 1.1.0", + "equivalent", "hashbrown", ] @@ -1012,18 +1135,33 @@ dependencies = [ [[package]] name = "ipnetwork" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f84f1612606f3753f205a4e9a2efd6fe5b4c573a6269b2cc6c3003d44a0d127" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jpeg-decoder" @@ -1034,50 +1172,42 @@ dependencies = [ "rayon", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libdbus-sys" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" dependencies = [ "pkg-config", ] [[package]] -name = "log" -version = "0.3.9" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "log 0.4.17", + "bitflags 2.6.0", + "libc", ] [[package]] name = "log" -version = "0.4.17" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lzw" @@ -1094,62 +1224,48 @@ dependencies = [ "libc", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "mime" -version = "0.2.6" +name = "miniz_oxide" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "log 0.3.9", + "adler", + "simd-adler32", ] [[package]] name = "miniz_oxide" -version = "0.3.7" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler32", + "adler2", ] [[package]] name = "mio" -version = "0.8.4" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", - "log 0.4.17", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "muldiv" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5136edda114182728ccdedb9f5eda882781f35fa6e80cc360af12a8932507f3" +checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" [[package]] name = "no-std-net" @@ -1157,6 +1273,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-derive" version = "0.2.5" @@ -1170,21 +1296,20 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg 1.1.0", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -1195,47 +1320,35 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg 1.1.0", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", @@ -1250,60 +1363,70 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.13.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "opaque-debug" -version = "0.2.3" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "option-operations" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b01597916c91a493b1e8a2fde64fec1764be3259abc1f06efc99c274f150a2" +checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" dependencies = [ "paste", ] [[package]] -name = "paste" -version = "1.0.7" +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] -name = "percent-encoding" -version = "1.0.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.2.1" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.2.1" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13570633aff33c6d22ce47dd566b10a3b9122c2fe9d8e7501895905be532b91" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -1311,33 +1434,53 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.2.1" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c567e5702efdc79fb18859ea74c3eb36e14c43da7b8c1f098a4ed6514ec7a0" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "pest_meta" -version = "2.2.1" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb32be5ee3bbdafa8c7a18b0a8a8d962b66cfa2ceee4037f49267a50ee821fe" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", - "sha-1 0.10.0", + "sha2", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1347,24 +1490,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "pnet_base" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d3a993d49e5fd5d4d854d6999d4addca1f72d86c65adf224a36757161c02b6" +checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7" dependencies = [ "no-std-net", ] [[package]] name = "pnet_datalink" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e466faf03a98ad27f6e15cd27a2b7cc89e73e640a43527742977bc503c37f8aa" +checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7" dependencies = [ "ipnetwork", "libc", @@ -1375,9 +1518,9 @@ dependencies = [ [[package]] name = "pnet_sys" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "328e231f0add6d247d82421bf3790b4b33b39c8930637f428eef24c4c6a90805" +checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b" dependencies = [ "libc", "winapi", @@ -1389,69 +1532,41 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef859a23054bbfee7811284275ae522f0434a3c8e7f4b74bd4a35ae7e1c4a283" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", - "deflate 0.7.20", + "deflate", "inflate", ] [[package]] name = "png" -version = "0.16.8" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", - "deflate 0.8.6", - "miniz_oxide", + "fdeflate", + "flate2", + "miniz_oxide 0.7.4", ] [[package]] name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "pretty-hex" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" - -[[package]] -name = "proc-macro-crate" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d50bfb8c23f23915855a00d98b5a35ef2e0b871bb52937bacadb798fbb66c8" -dependencies = [ - "once_cell", - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "proc-macro-error-attr", - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", - "version_check 0.9.4", + "zerocopy", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-crate" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "proc-macro2 1.0.42", - "quote 1.0.20", - "version_check 0.9.4", + "toml_edit", ] [[package]] @@ -1465,21 +1580,20 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.42" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "qrcode" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d2f1455f3630c6e5107b4f2b94e74d76dea80736de0981fd27644216cff57f" +checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" dependencies = [ - "checked_int_cast", - "image 0.23.14", + "image 0.25.2", ] [[package]] @@ -1493,30 +1607,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" -dependencies = [ - "proc-macro2 1.0.42", -] - -[[package]] -name = "rand" -version = "0.6.5" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", + "proc-macro2 1.0.86", ] [[package]] @@ -1529,17 +1624,18 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", ] [[package]] -name = "rand_chacha" -version = "0.1.1" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] @@ -1553,20 +1649,15 @@ dependencies = [ ] [[package]] -name = "rand_core" +name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "rand_core 0.4.2", + "ppv-lite86", + "rand_core 0.6.4", ] -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" @@ -1577,12 +1668,12 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.1.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.3.1", + "getrandom 0.2.15", ] [[package]] @@ -1594,123 +1685,48 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rayon" -version = "1.5.3" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ - "autocfg 1.1.0", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.7", - "redox_syscall", + "getrandom 0.2.15", + "libredox", "thiserror", ] [[package]] -name = "ryu" -version = "1.0.10" +name = "rustc-demangle" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "safemem" -version = "0.3.3" +name = "ryu" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scoped_threadpool" @@ -1720,128 +1736,150 @@ checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.141" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af873f2c95b99fcb0bd0fe622a43e29514658873c8ceba88c4cb88833a22500" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.141" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] -name = "sha-1" -version = "0.8.2" +name = "serde_spanned" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug", + "serde", ] [[package]] -name = "sha-1" -version = "0.10.0" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest", ] [[package]] -name = "sharded-slab" -version = "0.1.4" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "lazy_static", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "slab" -version = "0.4.7" +name = "sharded-slab" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "autocfg 1.1.0", + "lazy_static", ] [[package]] -name = "smallvec" -version = "1.9.0" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "socket2" -version = "0.4.4" +name = "signal-hook" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", - "winapi", + "signal-hook-registry", ] [[package]] -name = "strsim" -version = "0.8.0" +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] -name = "structopt" -version = "0.3.26" +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "clap", - "lazy_static", - "structopt-derive", + "autocfg", ] [[package]] -name = "structopt-derive" -version = "0.4.18" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "libc", + "windows-sys 0.52.0", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "0.15.44" @@ -1855,63 +1893,61 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ - "proc-macro2 1.0.42", - "quote 1.0.20", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "system-deps" -version = "6.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709" +checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" dependencies = [ "cfg-expr", - "heck 0.4.0", + "heck", "pkg-config", "toml", "version-compare", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "target-lexicon" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ + "cfg-if", "once_cell", ] @@ -1927,98 +1963,101 @@ dependencies = [ "num-traits", ] -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.20.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ - "autocfg 1.1.0", + "backtrace", "bytes", "libc", - "memchr", "mio", - "num_cpus", - "once_cell", "pin-project-lite", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "tokio-util" -version = "0.7.3" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.5.9" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "tower-service" -version = "0.3.2" +name = "toml_datetime" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2026,20 +2065,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.42", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2057,11 +2096,11 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "ansi_term", + "nu-ansi-term", "serde", "serde_json", "sharded-slab", @@ -2070,84 +2109,51 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" -version = "0.15.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" - -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" - -[[package]] -name = "unicase" -version = "1.4.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -dependencies = [ - "version_check 0.1.5", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.2" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - [[package]] name = "unicode-xid" version = "0.1.0" @@ -2156,58 +2162,51 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "url" -version = "1.7.2" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", + "form_urlencoded", + "idna", + "percent-encoding", ] [[package]] -name = "url" -version = "2.2.2" +name = "utf-8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna 0.2.3", - "matches", - "percent-encoding 2.1.0", -] +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "valuable" -version = "0.1.0" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "version-compare" +name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] -name = "version_check" -version = "0.1.5" +name = "version-compare" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log 0.4.17", "try-lock", ] @@ -2217,76 +2216,53 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "websocket" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92aacab060eea423e4036820ddd28f3f9003b2c4d8048cbda985e5a14e18038d" -dependencies = [ - "hyper 0.10.16", - "rand 0.6.5", - "unicase", - "url 1.7.2", - "websocket-base", -] - -[[package]] -name = "websocket-base" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49aec794b07318993d1db16156d5a9c750120597a5ee40c6b928d416186cb138" -dependencies = [ - "base64 0.10.1", - "bitflags", - "byteorder", - "rand 0.6.5", - "sha-1 0.8.2", -] - [[package]] name = "weylus" version = "0.11.4" dependencies = [ "autopilot", - "bitflags", + "bitflags 2.6.0", + "bytes", + "captrs", "cc", - "core-foundation 0.9.3", - "core-graphics 0.22.3", + "clap", + "clap_complete", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "dbus", "dirs", + "fastwebsockets", "fltk", + "fltk-theme", "gstreamer", "gstreamer-app", "gstreamer-video", "handlebars", - "hyper 0.14.20", + "http-body-util", + "hyper", + "hyper-util", "image 0.22.5", - "image 0.23.14", + "image 0.25.2", "num_cpus", - "percent-encoding 2.1.0", + "percent-encoding", "pnet_datalink", "qrcode", "serde", "serde_json", - "structopt", + "signal-hook", "tokio", "toml", "tracing", "tracing-subscriber", - "url 2.2.2", - "websocket", + "url", + "winapi", + "wio", ] [[package]] @@ -2313,53 +2289,198 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows-targets 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c52ac009d615e79296318c1bcce2d422aaca15ad08515e344feeda07df67a587" +dependencies = [ + "memchr", +] + +[[package]] +name = "wio" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] [[package]] name = "x11" -version = "2.19.1" +version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd0565fa8bfba8c5efe02725b14dff114c866724eff2cfd44d76cea74bcd87a" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" dependencies = [ "libc", "pkg-config", ] + +[[package]] +name = "x11cap" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ccedf556cb1f784d462dd8f24a7804f766d57c0051439d3e4052465263c399" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] diff --git a/Cargo.toml b/Cargo.toml index e80fdae5..b3e6c92f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,41 +8,52 @@ description = "Use your iPad or Android tablet as graphic tablet." [dependencies] autopilot = { git = "https://github.com/H-M-H/autopilot-rs.git", rev = "63eed09c715bfb665bb23172a3930a528e11691c" } -bitflags = "^1.3" -dirs = "^4.0" -fltk = { version = "^1", features = ["no-pango"] } -handlebars = "^4.1" -hyper = { version = "^0.14", features = ["server", "tcp", "http1", "http2"] } -image = { version = "^0.23", features = ["png"], default-features = false } +bitflags = { version = "^2.6", features = ["serde"] } +bytes = "1.7.1" +clap = { version = "4.5.18", features = ["derive"] } +clap_complete = "4.5.29" +dirs = "^5.0" +fastwebsockets = { version = "0.8.0", features = ["upgrade", "unstable-split"] } +fltk = { version = "^1" } +fltk-theme = "0.7.3" +handlebars = "^6.1" +http-body-util = "0.1.2" +hyper = { version = "^1.4", features = ["server", "http1", "http2"] } +hyper-util = { version = "0.1.8", features = ["tokio"] } +image = { version = "^0.25", features = ["png"], default-features = false } image_autopilot = { package = "image", version = "0.22.5", features = [], default-features = false } percent-encoding = "2.1.0" -qrcode = "0.12.0" +qrcode = "0.14.0" serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" -structopt = { version = "^0.3", features = ["color", "suggestions"], default-features = false } -tokio = { version = "^1", features = ["macros", "rt-multi-thread", "sync"] } -toml = "^0.5" +signal-hook = "0.3.17" +tokio = { version = "^1", features = ["fs", "macros", "rt-multi-thread", "sync"] } +toml = "^0.8" tracing = "^0.1" tracing-subscriber = { version = "^0.3", features = ["ansi", "json"], default-features = false } -url = "^2.2" -websocket = { version = "=0.26.5", features = ["sync"], default-features = false } +url = "^2.5" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.9", features = ["d3d11", "d3dcommon", "dxgi", "dxgi1_2", "dxgitype"] } +wio = "0.2.2" +captrs = "^0.3.1" [build-dependencies] -cc = "^1.0" -num_cpus = "^1.13" +cc = "^1.1" +num_cpus = "^1.16" [target.'cfg(target_os = "linux")'.dependencies] dbus = "^0.9" -gstreamer = "^0.18" -gstreamer-app = { version = "^0.18", features = ["v1_10"] } -gstreamer-video = "^0.18" +gstreamer = "^0.23" +gstreamer-app = { version = "^0.23", features = ["v1_16"] } +gstreamer-video = "^0.23" [target.'cfg(not(target_os = "windows"))'.dependencies] -pnet_datalink = "^0.31" +pnet_datalink = "^0.35" [target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "^0.9" -core-graphics = "^0.22" +core-foundation = "^0.10" +core-graphics = "^0.24" [features] bench = [] @@ -55,9 +66,6 @@ identifier = "io.github.h-m-h.weylus" [package.metadata.deb] name = "Weylus" -# Until https://github.com/mmstick/cargo-deb/issues/170 is resolved -# specify depends manually. -depends = "libc6 (>= 2.18), libdbus-1-3 (>= 1.9.14), libdrm2 (>= 2.4.60), libfontconfig1 (>= 2.11), libgcc1 (>= 1:4.2), libglib2.0-0 (>= 2.18.0), libgstreamer-plugins-base1.0-0 (>= 1.10.0), libgstreamer1.0-0 (>= 1.0.0), libx11-6 (>= 2:1.4.99.1), libxcomposite1 (>= 1:0.3-1), libxcursor1 (>> 1.1.2), libxext6, libxfixes3, libxft2 (>> 2.1.1), libxi6 (>= 2:1.2.99.4), libxinerama1, libxrandr2 (>= 2:1.5.0), libxrender1, libxtst6" section = "graphics" priority = "optional" assets = [ diff --git a/Readme.md b/Readme.md index b381caac..31d6e995 100644 --- a/Readme.md +++ b/Readme.md @@ -270,7 +270,20 @@ libxfixes-dev libxtst-dev libxrandr-dev libxcomposite-dev libxi-dev libxv-dev au nvidia-cuda-dev pkg-config libdrm-dev libpango1.0-dev libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev libdbus-1-dev ``` -Note that building for the first time may take a while as by default ffmpeg needs to be build. On + +On Fedora, they can be installed via: +```sh +sudo dnf install libXext-devel libXft-devel libXinerama-devel libXcursor-devel libXrender-devel \ +libXfixes-devel libXtst-devel libXrandr-devel libXcomposite-devel libXi-devel libXv-devel autoconf libtool \ +pkg-config libdrm-devel pango-devel gstreamer1-devel \ +gstreamer1-plugins-base-devel dbus-devel nasm npm +``` +After npm is installed, typescript must be installed by: +```sh +sudo npm install typescript -g +``` + +Note that building for the first time may take a while as by default ffmpeg needs to be built. On Windows only msvc is supported as C compiler; it is, however, possible to cross compile on Linux for Windows using minGW. diff --git a/build.rs b/build.rs index 35787941..e63ee819 100644 --- a/build.rs +++ b/build.rs @@ -169,4 +169,7 @@ fn linux() { println!("cargo:rustc-link-lib={}=va-drm", va_link_kind); println!("cargo:rustc-link-lib={}=va-x11", va_link_kind); println!("cargo:rustc-link-lib=drm"); + println!("cargo:rustc-link-lib=xcb-dri3"); + println!("cargo:rustc-link-lib=X11-xcb"); + println!("cargo:rustc-link-lib=xcb"); } diff --git a/build_in_local_container.sh b/build_in_local_container.sh new file mode 100755 index 00000000..2e9b1c30 --- /dev/null +++ b/build_in_local_container.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env sh + +set -ex + +rm -f docker/archive.tar.gz +git ls-files | tar Tczf - docker/archive.tar.gz + +podman run --replace -d --name weylus_build hhmhh/weylus_build:latest sleep infinity +podman cp docker/archive.tar.gz weylus_build:/ +podman exec weylus_build sh -c "mkdir /weylus && tar xf archive.tar.gz --directory=/weylus && cd weylus && ./docker_build.sh" + +podman run --replace -d --name weylus_build_alpine hhmhh/weylus_build_alpine:latest sleep infinity +podman cp docker/archive.tar.gz weylus_build_alpine:/ +podman exec weylus_build_alpine sh -c "mkdir /weylus && tar xf archive.tar.gz --directory=/weylus && cd weylus && RUSTFLAGS='-C target-feature=-crt-static' cargo build --release" diff --git a/deps/build.sh b/deps/build.sh index a1bfee8c..e8cc6b5b 100755 --- a/deps/build.sh +++ b/deps/build.sh @@ -16,7 +16,7 @@ if [ "$OS" == "Windows_NT" ]; then export HOST_OS="windows" fi -[ -z "$DIST" ] && export DIST="dist" +[ -z "$DIST" ] && export DIST="$PWD/dist" [ -z "$TARGET_OS" ] && export TARGET_OS="$HOST_OS" export NPROCS="$(nproc || echo 4)" diff --git a/deps/download.sh b/deps/download.sh index ab45dcd2..28916047 100755 --- a/deps/download.sh +++ b/deps/download.sh @@ -3,10 +3,10 @@ set -ex test -d x264 || git clone --depth 1 -b stable https://code.videolan.org/videolan/x264.git x264 -test -d ffmpeg || git clone --depth 1 -b n5.1 https://git.ffmpeg.org/ffmpeg.git ffmpeg +test -d ffmpeg || git clone --depth 1 -b n7.0.2 https://git.ffmpeg.org/ffmpeg.git ffmpeg if [ "$TARGET_OS" == "linux" ]; then test -d nv-codec-headers || git clone --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git - test -d libva || git clone --depth 1 -b 2.15.0 https://github.com/intel/libva + test -d libva || git clone --depth 1 -b 2.22.0 https://github.com/intel/libva fi if [ "$TARGET_OS" == "windows" ]; then test -d nv-codec-headers || git clone --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git diff --git a/docker/Dockerfile b/docker/Dockerfile index b464ccff..ed4763bd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,21 +1,21 @@ -FROM debian:stretch +FROM debian:bullseye ENV RUSTUP_HOME="/usr/local/rustup" CARGO_HOME="/usr/local/cargo" PATH="/usr/local/cargo/bin:$PATH" RUN apt-get update && \ apt-get install -y libx11-dev libxext-dev libxft-dev libxinerama-dev libxcursor-dev \ libxrender-dev libxfixes-dev libgl1-mesa-dev libglu1-mesa-dev libxtst-dev cmake git curl \ software-properties-common zip libssl-dev libxrandr-dev libxcomposite-dev libxi-dev \ - gcc g++ autoconf libtool-bin libxv-dev libdrm-dev libpango1.0-dev pkg-config \ - libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libdbus-1-dev mingw-w64 + gcc g++ autoconf libtool-bin libxv-dev libdrm-dev libpango1.0-dev pkg-config mingw-w64 \ + libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libdbus-1-dev libxcb-dri3-dev RUN apt-add-repository contrib RUN apt-add-repository non-free RUN apt-get update && apt-get install -y nvidia-cuda-dev -RUN curl -Lo cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.23.3/cmake-3.23.3.tar.gz && tar xf cmake.tar.gz +RUN curl -Lo cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.30.3/cmake-3.30.3.tar.gz && tar xf cmake.tar.gz RUN cd cmake-3.* && cmake . && make -j$(nproc) && make install RUN rm -rf cmake* -RUN curl -LO "https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.xz" && \ - tar xf "nasm-2.15.05.tar.xz" && cd "nasm-2.15.05" && \ - ./configure --prefix=/usr && make -j$(nproc) && make install && cd .. && rm -rf "nasm-2.15.05*" -RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ +RUN curl -LO "https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/nasm-2.16.03.tar.xz" && \ + tar xf "nasm-2.16.03.tar.xz" && cd "nasm-2.16.03" && \ + ./configure --prefix=/usr && make -j$(nproc) && make install && cd .. && rm -rf "nasm-2.16.03*" +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ apt-get install -y nodejs && \ npm install -g typescript RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ diff --git a/lib/encode_video.c b/lib/encode_video.c index 2299552e..68b96ab3 100644 --- a/lib/encode_video.c +++ b/lib/encode_video.c @@ -310,11 +310,12 @@ void open_video(VideoContext* ctx, Error* err) if (ctx->c) { ctx->sw_pix_fmt = ctx->c->pix_fmt = AV_PIX_FMT_BGR0; - av_opt_set(ctx->c->priv_data, "preset", "fast", 0); + av_opt_set(ctx->c->priv_data, "preset", "p1", 0); av_opt_set(ctx->c->priv_data, "zerolatency", "1", 0); av_opt_set(ctx->c->priv_data, "tune", "ull", 0); - av_opt_set(ctx->c->priv_data, "rc", "vbr", 0); + av_opt_set(ctx->c->priv_data, "rc", "cbr", 0); av_opt_set(ctx->c->priv_data, "cq", "21", 0); + av_opt_set(ctx->c->priv_data, "delay", "0", 0); set_codec_params(ctx); int ret = avcodec_open2(ctx->c, codec, NULL); if (ret == 0) diff --git a/lib/linux/xcapture.c b/lib/linux/xcapture.c index cff81da6..743021e4 100644 --- a/lib/linux/xcapture.c +++ b/lib/linux/xcapture.c @@ -13,6 +13,7 @@ #include #include "../error.h" +#include "../log.h" #include "xhelper.h" int clamp(int x, int lb, int ub) @@ -226,43 +227,51 @@ void capture_screen(CaptureContext* ctx, struct Image* img, int capture_cursor, if (capture_cursor && ctx->has_xfixes) { XFixesCursorImage* cursor_img = XFixesGetCursorImage(ctx->cap.disp); - uint32_t* data = (uint32_t*)ctx->ximg->data; + if (cursor_img != NULL) + { + uint32_t* data = (uint32_t*)ctx->ximg->data; - // coordinates of cursor inside ximg - int x0 = cursor_img->x - cursor_img->xhot - x; - int y0 = cursor_img->y - cursor_img->yhot - y; + // coordinates of cursor inside ximg + int x0 = cursor_img->x - cursor_img->xhot - x; + int y0 = cursor_img->y - cursor_img->yhot - y; - // clamp part of cursor image to draw to the part of the cursor that is inside - // the captured area - int i0 = clamp(0, -x0, width - x0); - int i1 = clamp(cursor_img->width, -x0, width - x0); - int j0 = clamp(0, -y0, height - y0); - int j1 = clamp(cursor_img->height, -y0, height - y0); - // paint cursor image into captured image - for (int j = j0; j < j1; ++j) - for (int i = i0; i < i1; ++i) - { - uint32_t c_pixel = cursor_img->pixels[j * cursor_img->width + i]; - unsigned char a = (c_pixel & 0xff000000) >> 24; - if (a) + // clamp part of cursor image to draw to the part of the cursor that is inside + // the captured area + int i0 = clamp(0, -x0, width - x0); + int i1 = clamp(cursor_img->width, -x0, width - x0); + int j0 = clamp(0, -y0, height - y0); + int j1 = clamp(cursor_img->height, -y0, height - y0); + // paint cursor image into captured image + for (int j = j0; j < j1; ++j) + for (int i = i0; i < i1; ++i) { - uint32_t d_pixel = data[(j + y0) * width + i + x0]; + uint32_t c_pixel = cursor_img->pixels[j * cursor_img->width + i]; + unsigned char a = (c_pixel & 0xff000000) >> 24; + if (a) + { + uint32_t d_pixel = data[(j + y0) * width + i + x0]; - unsigned char c1 = (c_pixel & 0x00ff0000) >> 16; - unsigned char c2 = (c_pixel & 0x0000ff00) >> 8; - unsigned char c3 = (c_pixel & 0x000000ff) >> 0; - unsigned char d1 = (d_pixel & 0x00ff0000) >> 16; - unsigned char d2 = (d_pixel & 0x0000ff00) >> 8; - unsigned char d3 = (d_pixel & 0x000000ff) >> 0; - // colors from the cursor image are premultiplied with the alpha channel - unsigned char f1 = c1 + d1 * (255 - a) / 255; - unsigned char f2 = c2 + d2 * (255 - a) / 255; - unsigned char f3 = c3 + d3 * (255 - a) / 255; - data[(j + y0) * width + i + x0] = (f1 << 16) | (f2 << 8) | (f3 << 0); + unsigned char c1 = (c_pixel & 0x00ff0000) >> 16; + unsigned char c2 = (c_pixel & 0x0000ff00) >> 8; + unsigned char c3 = (c_pixel & 0x000000ff) >> 0; + unsigned char d1 = (d_pixel & 0x00ff0000) >> 16; + unsigned char d2 = (d_pixel & 0x0000ff00) >> 8; + unsigned char d3 = (d_pixel & 0x000000ff) >> 0; + // colors from the cursor image are premultiplied with the alpha channel + unsigned char f1 = c1 + d1 * (255 - a) / 255; + unsigned char f2 = c2 + d2 * (255 - a) / 255; + unsigned char f3 = c3 + d3 * (255 - a) / 255; + data[(j + y0) * width + i + x0] = (f1 << 16) | (f2 << 8) | (f3 << 0); + } } - } - XFree(cursor_img); + XFree(cursor_img); + } + else + { + log_warn( + "Failed to obtain cursor image, XFixesGetCursorImage has returned a null pointer."); + } } img->width = ctx->ximg->width; diff --git a/src/capturable/autopilot.rs b/src/capturable/autopilot.rs index 719daad6..3eb51d21 100644 --- a/src/capturable/autopilot.rs +++ b/src/capturable/autopilot.rs @@ -3,7 +3,7 @@ use std::error::Error; use image_autopilot::GenericImageView; -use crate::capturable::{Capturable, Recorder}; +use crate::capturable::{Capturable, Geometry, Recorder}; #[derive(Clone)] pub struct AutoPilotCapturable {} @@ -18,8 +18,8 @@ impl Capturable for AutoPilotCapturable { fn name(&self) -> String { "Desktop (autopilot)".into() } - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box> { - Ok((0.0, 0.0, 1.0, 1.0)) + fn geometry(&self) -> Result> { + Ok(Geometry::Relative(0.0, 0.0, 1.0, 1.0)) } fn before_input(&mut self) -> Result<(), Box> { Ok(()) diff --git a/src/capturable/captrs_capture.rs b/src/capturable/captrs_capture.rs new file mode 100644 index 00000000..244f60d6 --- /dev/null +++ b/src/capturable/captrs_capture.rs @@ -0,0 +1,84 @@ +use crate::capturable::{Capturable, Recorder}; +use captrs::Capturer; +use std::boxed::Box; +use std::error::Error; +use winapi::shared::windef::RECT; + +use super::Geometry; + +#[derive(Clone)] +pub struct CaptrsCapturable { + id: u8, + name: String, + screen: RECT, + virtual_screen: RECT, +} + +impl CaptrsCapturable { + pub fn new(id: u8, name: String, screen: RECT, virtual_screen: RECT) -> CaptrsCapturable { + CaptrsCapturable { + id, + name, + screen, + virtual_screen, + } + } +} + +impl Capturable for CaptrsCapturable { + fn name(&self) -> String { + format!("Desktop {} (captrs)", self.name).into() + } + fn before_input(&mut self) -> Result<(), Box> { + Ok(()) + } + fn recorder(&self, _capture_cursor: bool) -> Result, Box> { + Ok(Box::new(CaptrsRecorder::new(self.id)?)) + } + fn geometry(&self) -> Result> { + Ok(Geometry::VirtualScreen( + self.screen.left - self.virtual_screen.left, + self.screen.top - self.virtual_screen.top, + (self.screen.right - self.screen.left) as u32, + (self.screen.bottom - self.screen.top) as u32, + self.screen.left, + self.screen.top, + )) + } +} +#[derive(Debug)] +pub struct CaptrsError(String); + +impl std::fmt::Display for CaptrsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self(s) = self; + write!(f, "{}", s) + } +} + +impl Error for CaptrsError {} +pub struct CaptrsRecorder { + capturer: Capturer, +} + +impl CaptrsRecorder { + pub fn new(id: u8) -> Result> { + Ok(CaptrsRecorder { + capturer: Capturer::new(id.into())?, + }) + } +} + +impl Recorder for CaptrsRecorder { + fn capture(&mut self) -> Result> { + self.capturer + .capture_store_frame() + .map_err(|_e| CaptrsError("Captrs failed to capture frame".into()))?; + let (w, h) = self.capturer.geometry(); + Ok(crate::video::PixelProvider::BGR0( + w as usize, + h as usize, + unsafe { std::mem::transmute(self.capturer.get_stored_frame().unwrap()) }, + )) + } +} diff --git a/src/capturable/core_graphics.rs b/src/capturable/core_graphics.rs index 188efcc8..1cab85f7 100644 --- a/src/capturable/core_graphics.rs +++ b/src/capturable/core_graphics.rs @@ -19,7 +19,7 @@ use core_graphics::{ window::CGWindowID, }; -use crate::capturable::{Capturable, Recorder}; +use crate::capturable::{Capturable, Geometry, Recorder}; #[derive(Debug)] pub struct CGError(String); @@ -52,10 +52,10 @@ impl Capturable for CGDisplayCapturable { self.display.pixels_high() ) } - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box> { + fn geometry(&self) -> Result> { let bounds = self.display.bounds(); let (x0, y0, w, h) = screen_coordsys()?; - Ok(( + Ok(Geometry::Relative( (bounds.origin.x - x0) / w, (bounds.origin.y - y0) / h, bounds.size.width / w, @@ -175,8 +175,9 @@ impl Capturable for CGWindowCapturable { fn name(&self) -> String { self.name.clone() } - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box> { - Ok(self.geometry_relative) + fn geometry(&self) -> Result> { + let (x, y, w, h) = self.geometry_relative; + Ok(Geometry::Relative(x, y, w, h)) } fn before_input(&mut self) -> Result<(), Box> { self.update_geometry() diff --git a/src/capturable/mod.rs b/src/capturable/mod.rs index b758dc79..a81f12db 100644 --- a/src/capturable/mod.rs +++ b/src/capturable/mod.rs @@ -1,4 +1,5 @@ pub mod autopilot; + use std::boxed::Box; use std::error::Error; use tracing::warn; @@ -10,9 +11,13 @@ pub mod pipewire; #[cfg(target_os = "linux")] pub mod pipewire_dbus; pub mod testsrc; + +#[cfg(target_os = "windows")] +pub mod captrs_capture; +#[cfg(target_os = "windows")] +pub mod win_ctx; #[cfg(target_os = "linux")] pub mod x11; - pub trait Recorder { fn capture(&mut self) -> Result>; } @@ -29,14 +34,20 @@ where Box::new(self.clone()) } } +/// Relative: x, y, width, height of the Capturable as floats relative to the absolute size of the +/// screen. For example x=0.5, y=0.0, width=0.5, height=1.0 means the right half of the screen. +/// VirtualScreen: offset_x, offset_y, width, height for a capturable using a virtual screen. (Windows) +pub enum Geometry { + Relative(f64, f64, f64, f64), + VirtualScreen(i32, i32, u32, u32, i32, i32), +} pub trait Capturable: Send + BoxCloneCapturable { /// Name of the Capturable, for example the window title, if it is a window. fn name(&self) -> String; - /// Return x, y, width, height of the Capturable as floats relative to the absolute size of the - /// screen. For example x=0.5, y=0.0, width=0.5, height=1.0 means the right half of the screen. - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box>; + /// Return Geometry of the Capturable. + fn geometry(&self) -> Result>; /// Callback that is called right before input is simulated. /// Useful to focus the window on input. @@ -112,8 +123,21 @@ pub fn get_capturables( } } - use crate::capturable::autopilot::AutoPilotCapturable; - capturables.push(Box::new(AutoPilotCapturable::new())); + #[cfg(target_os = "windows")] + { + use crate::capturable::captrs_capture::CaptrsCapturable; + use crate::capturable::win_ctx::WinCtx; + let winctx = WinCtx::new(); + for (i, o) in winctx.get_outputs().iter().enumerate() { + let captr = CaptrsCapturable::new( + i as u8, + String::from_utf16_lossy(o.DeviceName.as_ref()), + o.DesktopCoordinates, + winctx.get_union_rect().clone(), + ); + capturables.push(Box::new(captr)); + } + } if crate::log::get_log_level() >= tracing::Level::DEBUG { for (width, height) in [ diff --git a/src/capturable/pipewire.rs b/src/capturable/pipewire.rs index c50a6790..8acc02d2 100644 --- a/src/capturable/pipewire.rs +++ b/src/capturable/pipewire.rs @@ -16,7 +16,7 @@ use gstreamer as gst; use gstreamer::prelude::*; use gstreamer_app::AppSink; -use crate::capturable::{Capturable, Recorder}; +use crate::capturable::{Capturable, Geometry, Recorder}; use crate::video::PixelProvider; use crate::capturable::pipewire_dbus::{ @@ -96,8 +96,8 @@ impl Capturable for PipeWireCapturable { format!("Pipewire {}, path: {}", type_str, self.path) } - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box> { - Ok((0.0, 0.0, 1.0, 1.0)) + fn geometry(&self) -> Result> { + Ok(Geometry::Relative(0.0, 0.0, 1.0, 1.0)) } fn before_input(&mut self) -> Result<(), Box> { @@ -122,9 +122,9 @@ pub struct PipeWireRecorder { impl PipeWireRecorder { pub fn new(capturable: PipeWireCapturable) -> Result> { - let pipeline = gst::Pipeline::new(None); + let pipeline = gst::Pipeline::new(); - let src = gst::ElementFactory::make("pipewiresrc", None)?; + let src = gst::ElementFactory::make("pipewiresrc").build()?; src.set_property("fd", &capturable.fd.as_raw_fd()); src.set_property("path", &format!("{}", capturable.path)); @@ -132,7 +132,7 @@ impl PipeWireRecorder { // see: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/982 src.set_property("always-copy", &true); - let sink = gst::ElementFactory::make("appsink", None)?; + let sink = gst::ElementFactory::make("appsink").build()?; sink.set_property("drop", &true); sink.set_property("max-buffers", &1u32); @@ -142,13 +142,13 @@ impl PipeWireRecorder { .dynamic_cast::() .map_err(|_| GStreamerError("Sink element is expected to be an appsink!".into()))?; let mut caps = gst::Caps::new_empty(); - caps.merge_structure(gst::structure::Structure::new( + caps.merge_structure(gst::structure::Structure::from_iter( "video/x-raw", - &[("format", &"BGRx")], + [("format", "BGRx".into())], )); - caps.merge_structure(gst::structure::Structure::new( + caps.merge_structure(gst::structure::Structure::from_iter( "video/x-raw", - &[("format", &"RGBx")], + [("format", "RGBx".into())], )); appsink.set_caps(Some(&caps)); diff --git a/src/capturable/testsrc.rs b/src/capturable/testsrc.rs index e9c630f0..9e86491a 100644 --- a/src/capturable/testsrc.rs +++ b/src/capturable/testsrc.rs @@ -1,4 +1,4 @@ -use crate::capturable::{Capturable, Recorder}; +use crate::capturable::{Capturable, Geometry, Recorder}; use crate::video::PixelProvider; use std::error::Error; @@ -43,8 +43,8 @@ impl Capturable for TestCapturable { fn name(&self) -> String { format!("Test Source {}x{}", self.width, self.height) } - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box> { - Ok((1.0, 1.0, 1.0, 1.0)) + fn geometry(&self) -> Result> { + Ok(Geometry::Relative(0.0, 0.0, 1.0, 1.0)) } fn before_input(&mut self) -> Result<(), Box> { Ok(()) diff --git a/src/capturable/win_ctx.rs b/src/capturable/win_ctx.rs new file mode 100644 index 00000000..2b16d987 --- /dev/null +++ b/src/capturable/win_ctx.rs @@ -0,0 +1,91 @@ +use std::mem::zeroed; +use std::{mem, ptr}; +use winapi::shared::dxgi::{ + CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIOutput, IID_IDXGIFactory1, + DXGI_OUTPUT_DESC, +}; + +use winapi::shared::windef::*; +use winapi::shared::winerror::*; +use winapi::um::winuser::*; +use wio::com::ComPtr; + +// from https://github.com/bryal/dxgcap-rs/blob/009b746d1c19c4c10921dd469eaee483db6aa002/src/lib.r +fn hr_failed(hr: HRESULT) -> bool { + hr < 0 +} + +fn create_dxgi_factory_1() -> ComPtr { + unsafe { + let mut factory = ptr::null_mut(); + let hr = CreateDXGIFactory1(&IID_IDXGIFactory1, &mut factory); + if hr_failed(hr) { + panic!("Failed to create DXGIFactory1, {:x}", hr) + } else { + ComPtr::from_raw(factory as *mut IDXGIFactory1) + } + } +} + +fn get_adapter_outputs(adapter: &IDXGIAdapter1) -> Vec> { + let mut outputs = Vec::new(); + for i in 0.. { + unsafe { + let mut output = ptr::null_mut(); + if hr_failed(adapter.EnumOutputs(i, &mut output)) { + break; + } else { + let mut out_desc = zeroed(); + (*output).GetDesc(&mut out_desc); + if out_desc.AttachedToDesktop != 0 { + outputs.push(ComPtr::from_raw(output)) + } else { + break; + } + } + } + } + outputs +} + +#[derive(Clone)] +pub struct WinCtx { + outputs: Vec, + union_rect: RECT, +} + +impl WinCtx { + pub fn new() -> WinCtx { + let mut desktops: Vec = Vec::new(); + let mut union: RECT; + unsafe { + union = mem::zeroed(); + let factory = create_dxgi_factory_1(); + let mut adapter = ptr::null_mut(); + if factory.EnumAdapters1(0, &mut adapter) != DXGI_ERROR_NOT_FOUND { + let adp = ComPtr::from_raw(adapter); + let outputs = get_adapter_outputs(&adp); + for o in outputs { + let mut desc: DXGI_OUTPUT_DESC = mem::zeroed(); + o.GetDesc(ptr::addr_of_mut!(desc)); + desktops.push(desc); + UnionRect( + ptr::addr_of_mut!(union), + ptr::addr_of!(union), + ptr::addr_of!(desc.DesktopCoordinates), + ); + } + } + } + WinCtx { + outputs: desktops, + union_rect: union, + } + } + pub fn get_outputs(&self) -> &Vec { + &self.outputs + } + pub fn get_union_rect(&self) -> &RECT { + &self.union_rect + } +} diff --git a/src/capturable/x11.rs b/src/capturable/x11.rs index 6eb149c4..f8c12f95 100644 --- a/src/capturable/x11.rs +++ b/src/capturable/x11.rs @@ -1,4 +1,4 @@ -use crate::capturable::{Capturable, Recorder}; +use crate::capturable::{Capturable, Geometry, Recorder}; use crate::cerror::CError; use crate::video::PixelProvider; use std::ffi::{CStr, CString}; @@ -95,7 +95,7 @@ impl Capturable for X11Capturable { } } - fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box> { + fn geometry(&self) -> Result> { let mut x: c_float = 0.0; let mut y: c_float = 0.0; let mut width: c_float = 0.0; @@ -116,7 +116,12 @@ impl Capturable for X11Capturable { if err.is_err() { return Err(Box::new(err)); } - Ok((x.into(), y.into(), width.into(), height.into())) + Ok(Geometry::Relative( + x.into(), + y.into(), + width.into(), + height.into(), + )) } fn before_input(&mut self) -> Result<(), Box> { diff --git a/src/config.rs b/src/config.rs index 52d16c21..b2b26878 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,88 +1,152 @@ -use std::fs; use std::net::IpAddr; +use std::str::FromStr; +use std::{fs, path::PathBuf}; +use clap::Parser; use serde::{Deserialize, Serialize}; -use structopt::StructOpt; -use tracing::warn; +use tracing::{debug, warn}; -#[derive(Serialize, Deserialize, StructOpt, Debug, Clone)] -#[structopt(name = "weylus")] +#[derive(clap::ValueEnum, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +pub enum ThemeType { + Aero, + AquaClassic, + Blue, + Classic, + Dark, + Greybird, + HighContrast, + Metro, +} + +const THEME_LIST: [ThemeType; 8] = [ + ThemeType::Aero, + ThemeType::AquaClassic, + ThemeType::Blue, + ThemeType::Classic, + ThemeType::Dark, + ThemeType::Greybird, + ThemeType::HighContrast, + ThemeType::Metro, +]; + +impl Default for ThemeType { + fn default() -> Self { + Self::Greybird + } +} + +impl ThemeType { + pub fn apply(&self) { + let theme = match self { + ThemeType::Classic => fltk_theme::ThemeType::Classic, + ThemeType::Aero => fltk_theme::ThemeType::Aero, + ThemeType::Metro => fltk_theme::ThemeType::Metro, + ThemeType::AquaClassic => fltk_theme::ThemeType::AquaClassic, + ThemeType::Greybird => fltk_theme::ThemeType::Greybird, + ThemeType::Blue => fltk_theme::ThemeType::Blue, + ThemeType::Dark => fltk_theme::ThemeType::Dark, + ThemeType::HighContrast => fltk_theme::ThemeType::HighContrast, + }; + let theme = fltk_theme::WidgetTheme::new(theme); + theme.apply(); + } + + pub fn name(&self) -> String { + format!("{self:?}") + } + + pub fn to_index(&self) -> i32 { + THEME_LIST.iter().position(|th| th == self).unwrap() as i32 + } + + pub fn from_index(i: i32) -> Self { + let i = i.clamp(0, THEME_LIST.len() as i32 - 1) as usize; + THEME_LIST[i] + } + + pub fn themes() -> &'static [ThemeType] { + &THEME_LIST + } +} + +#[derive(Serialize, Deserialize, Parser, Debug, Clone)] +#[command(version, about, long_about = None)] pub struct Config { - #[structopt(long, help = "Access code")] + #[arg(long, help = "Access code")] pub access_code: Option, - #[structopt(long, default_value = "0.0.0.0", help = "Bind address")] + #[arg(long, default_value = "0.0.0.0", help = "Bind address")] pub bind_address: IpAddr, - #[structopt(long, default_value = "1701", help = "Web port")] + #[arg(long, default_value = "1701", help = "Web port")] pub web_port: u16, - #[structopt(long, default_value = "9001", help = "Websocket port")] - pub websocket_port: u16, #[cfg(target_os = "linux")] - #[structopt( + #[arg( long, help = "Try to use hardware acceleration through the Video Acceleration API." )] pub try_vaapi: bool, #[cfg(any(target_os = "linux", target_os = "windows"))] - #[structopt(long, help = "Try to use Nvidia's NVENC to encode the video via GPU.")] + #[arg(long, help = "Try to use Nvidia's NVENC to encode the video via GPU.")] #[serde(default)] pub try_nvenc: bool, #[cfg(target_os = "macos")] - #[structopt( + #[arg( long, help = "Try to use hardware acceleration through the VideoToolbox API." )] #[serde(default)] pub try_videotoolbox: bool, #[cfg(target_os = "windows")] - #[structopt( + #[arg( long, help = "Try to use hardware acceleration through the MediaFoundation API." )] #[serde(default)] pub try_mediafoundation: bool, - #[structopt(long, help = "Start Weylus server immediately on program start.")] + #[arg(long, help = "Start Weylus server immediately on program start.")] #[serde(default)] pub auto_start: bool, - #[structopt(long, help = "Run Weylus without gui and start immediately.")] + #[arg(long, help = "Gui Theme")] + pub gui_theme: Option, + #[arg(long, help = "Run Weylus without gui and start immediately.")] #[serde(default)] pub no_gui: bool, #[cfg(target_os = "linux")] - #[structopt(long, help = "Wayland/PipeWire Support.")] + #[arg(long, help = "Wayland/PipeWire Support.")] #[serde(default)] pub wayland_support: bool, - #[structopt(long, help = "Print template of index.html served by Weylus.")] + #[arg(long, help = "Print template of index.html served by Weylus.")] #[serde(skip)] pub print_index_html: bool, - #[structopt(long, help = "Print access.html served by Weylus.")] + #[arg(long, help = "Print access.html served by Weylus.")] #[serde(skip)] pub print_access_html: bool, - #[structopt(long, help = "Print style.css served by Weylus.")] + #[arg(long, help = "Print style.css served by Weylus.")] #[serde(skip)] pub print_style_css: bool, - #[structopt(long, help = "Print lib.js served by Weylus.")] + #[arg(long, help = "Print lib.js served by Weylus.")] #[serde(skip)] pub print_lib_js: bool, - #[structopt( + #[arg( long, help = "Use custom template of index.html to be served by Weylus." )] #[serde(skip)] - pub custom_index_html: Option, - #[structopt(long, help = "Use custom access.html to be served by Weylus.")] + pub custom_index_html: Option, + #[arg(long, help = "Use custom access.html to be served by Weylus.")] #[serde(skip)] - pub custom_access_html: Option, - #[structopt(long, help = "Use custom style.css to be served by Weylus.")] + pub custom_access_html: Option, + #[arg(long, help = "Use custom style.css to be served by Weylus.")] #[serde(skip)] - pub custom_style_css: Option, - #[structopt(long, help = "Use custom lib.js to be served by Weylus.")] + pub custom_style_css: Option, + #[arg(long, help = "Use custom lib.js to be served by Weylus.")] #[serde(skip)] - pub custom_lib_js: Option, + pub custom_lib_js: Option, - #[structopt(long, help = "Print shell completions for given shell.")] + #[arg(long, help = "Print shell completions for given shell.")] #[serde(skip)] - pub completions: Option, + pub completions: Option, } pub fn read_config() -> Option { @@ -98,7 +162,12 @@ pub fn read_config() -> Option { } }, Err(err) => { - warn!("Failed to read configuration file: {}", err); + match err.kind() { + std::io::ErrorKind::NotFound => { + debug!("Failed to read configuration file: {}", err) + } + _ => warn!("Failed to read configuration file: {}", err), + } None } } @@ -132,14 +201,13 @@ pub fn write_config(conf: &Config) { } pub fn get_config() -> Config { - // TODO: once https://github.com/clap-rs/clap/issues/748 is resolved use - // the configfile to provide default values that override hardcoded defaults - - // read config from file if no args are specified - if std::env::args().len() == 1 { - // (ab)use parsing an empty args array to provide a default config - read_config().unwrap_or_else(crate::config::Config::from_args) + let args = std::env::args(); + if let Some(mut config) = read_config() { + if args.len() > 1 { + config.update_from(args); + } + config } else { - crate::config::Config::from_args() + Config::parse() } } diff --git a/src/gui.rs b/src/gui.rs index cfe212a2..99bbfd42 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,11 +1,15 @@ +use std::cmp::min; +use std::io::Cursor; use std::iter::Iterator; use std::net::{IpAddr, SocketAddr}; +use fltk::image::PngImage; +use fltk::menu::Choice; use std::sync::{mpsc, Arc, Mutex}; use tracing::{error, info}; use fltk::{ - app::{App, awake_callback}, + app::{awake_callback, App}, button::{Button, CheckButton}, frame::Frame, input::{Input, IntInput}, @@ -18,8 +22,8 @@ use fltk::{ #[cfg(not(target_os = "windows"))] use pnet_datalink as datalink; -use crate::config::{write_config, Config}; -use crate::websocket::Ws2UiMessage; +use crate::config::{write_config, Config, ThemeType}; +use crate::web::Web2UiMessage::UInputInaccessible; pub fn run(config: &Config, log_receiver: mpsc::Receiver) { let width = 200; @@ -27,8 +31,9 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { let padding = 10; let app = App::default().with_scheme(fltk::app::AppScheme::Gtk); + config.gui_theme.map(|th| th.apply()); let mut wind = Window::default() - .with_size(660, 620) + .with_size(660, 600) .center_screen() .with_label(&format!("Weylus - {}", env!("CARGO_PKG_VERSION"))); wind.set_xclass("weylus"); @@ -59,15 +64,9 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { .with_label("Port"); input_port.set_value(&config.web_port.to_string()); - let mut input_ws_port = IntInput::default() - .with_size(width, height) - .below_of(&input_port, padding) - .with_label("Websocket Port"); - input_ws_port.set_value(&config.websocket_port.to_string()); - let mut check_auto_start = CheckButton::default() .with_size(70, height) - .below_of(&input_ws_port, padding) + .below_of(&input_port, padding + 5) .with_label("Auto Start"); check_auto_start.set_tooltip("Start Weylus server immediately on program start."); check_auto_start.set_checked(config.auto_start); @@ -158,10 +157,19 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { output.set_buffer(output_buf); let output_buf = output.buffer().unwrap(); - let mut qr_frame = Frame::default() - .with_size(240, 240) + let mut choice_theme = Choice::default() + .with_size(width, height) .right_of(&input_access_code, padding); + for theme in ThemeType::themes() { + choice_theme.add_choice(&theme.name()); + } + choice_theme.set_value(config.gui_theme.unwrap_or(ThemeType::default()).to_index()); + + let mut qr_frame = Frame::default() + .with_size(235, 235) + .right_of(&input_bind_addr, padding); + qr_frame.hide(); wind.make_resizable(true); @@ -180,10 +188,23 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { let mut weylus = crate::weylus::Weylus::new(); let mut is_server_running = false; let auto_start = config.auto_start; - let mut config = config.clone(); + let config = Arc::new(Mutex::new(config.clone())); + + { + let config = config.clone(); + choice_theme.set_callback(move |c| { + let v = c.value(); + if v >= 0 { + ThemeType::from_index(v).apply(); + config.lock().unwrap().gui_theme = Some(ThemeType::from_index(v)); + write_config(&config.lock().unwrap()); + } + }); + } let mut toggle_server = move |but: &mut Button| { if let Err(err) = || -> Result<(), Box> { + let mut config = config.lock().unwrap(); if !is_server_running { { let access_code_string = input_access_code.value(); @@ -193,13 +214,12 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { }; let bind_addr: IpAddr = input_bind_addr.value().parse()?; let web_port: u16 = input_port.value().parse()?; - let ws_port: u16 = input_ws_port.value().parse()?; config.access_code = access_code.map(|s| s.to_string()); config.web_port = web_port; - config.websocket_port = ws_port; config.bind_address = bind_addr; config.auto_start = check_auto_start.is_checked(); + config.gui_theme = Some(ThemeType::from_index(choice_theme.value())); #[cfg(target_os = "linux")] { config.try_vaapi = check_native_hw_accel.is_checked(); @@ -218,35 +238,28 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { config.try_mediafoundation = check_native_hw_accel.is_checked(); } } - if !weylus.start( - &config, - |_| {}, - |message| match message { - Ws2UiMessage::UInputInaccessible => { - awake_callback(move || { - let w = 500; - let h = 300; - let mut pop_up = Window::default() - .with_size(w, h) - .center_screen() - .with_label("Weylus - UInput inaccessible!"); - pop_up.set_xclass("weylus"); - - let buf = TextBuffer::default(); - let mut pop_up_text = TextDisplay::default().with_size(w, h); - pop_up_text.set_buffer(buf); - pop_up_text.wrap_mode(fltk::text::WrapMode::AtBounds, 5); - let mut buf = pop_up_text.buffer().unwrap(); - buf.set_text(std::include_str!("strings/uinput_error.txt")); - - pop_up.end(); - pop_up.make_modal(true); - pop_up.show(); - }); - } - _ => {} - }, - ) { + if !weylus.start(&config, |message| match message { + UInputInaccessible => awake_callback(move || { + let w = 500; + let h = 300; + let mut pop_up = Window::default() + .with_size(w, h) + .center_screen() + .with_label("Weylus - UInput inaccessible!"); + pop_up.set_xclass("weylus"); + + let buf = TextBuffer::default(); + let mut pop_up_text = TextDisplay::default().with_size(w, h); + pop_up_text.set_buffer(buf); + pop_up_text.wrap_mode(fltk::text::WrapMode::AtBounds, 5); + let mut buf = pop_up_text.buffer().unwrap(); + buf.set_text(std::include_str!("strings/uinput_error.txt")); + + pop_up.end(); + pop_up.make_modal(true); + pop_up.show(); + }), + }) { return Ok(()); } is_server_running = true; @@ -304,21 +317,29 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { .to_string(), ); } - let code = QrCode::new(&url_string).unwrap(); - let img_buf = code.render::>().build(); - let image = image::DynamicImage::ImageLuma8(img_buf); - let image = image.resize_exact( - qr_frame.width() as u32, - qr_frame.height() as u32, - image::imageops::FilterType::Nearest, - ); - let mut buf = vec![]; - image - .write_to(&mut buf, image::ImageOutputFormat::Png) - .unwrap(); - let png = fltk::image::PngImage::from_data(&buf).unwrap(); - - qr_frame.set_image(Some(png)); + + let cb = move |qr_frame: &mut Frame, _, _, w, h| { + let code = QrCode::new(&url_string).unwrap(); + let img_buf = code.render::>().build(); + let image = image::DynamicImage::ImageLuma8(img_buf); + let dims = min(w, h) as u32; + let image = + image.resize_exact(dims, dims, image::imageops::FilterType::Nearest); + let mut buf = vec![]; + let mut cursor = Cursor::new(&mut buf); + image + .write_to(&mut cursor, image::ImageFormat::Png) + .unwrap(); + let png = fltk::image::PngImage::from_data(&buf).unwrap(); + qr_frame.set_image(Some(png)); + }; + + let x = qr_frame.x(); + let y = qr_frame.y(); + let w = qr_frame.width(); + let h = qr_frame.height(); + cb(&mut qr_frame, x, y, w, h); + qr_frame.resize_callback(cb); qr_frame.show(); } #[cfg(target_os = "windows")] @@ -335,6 +356,7 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { weylus.stop(); but.set_label("Start"); output_server_addr.hide(); + qr_frame.resize_callback(|_, _, _, _, _| {}); qr_frame.hide(); is_server_running = false; } @@ -351,4 +373,8 @@ pub fn run(config: &Config, log_receiver: mpsc::Receiver) { but_toggle.set_callback(toggle_server); app.run().expect("Failed to run Gui!"); + + // TODO: Remove when https://github.com/fltk-rs/fltk-rs/issues/1480 is fixed + // this is required to drop the callback and do a graceful shutdown of the web server + but_toggle.set_callback(|_| ()); } diff --git a/src/input/autopilot_device.rs b/src/input/autopilot_device.rs index 81b670a1..0c9ee96f 100644 --- a/src/input/autopilot_device.rs +++ b/src/input/autopilot_device.rs @@ -8,7 +8,7 @@ use tracing::warn; use crate::input::device::{InputDevice, InputDeviceType}; use crate::protocol::{Button, KeyboardEvent, KeyboardEventType, PointerEvent, WheelEvent}; -use crate::capturable::Capturable; +use crate::capturable::{Capturable, Geometry}; pub struct AutoPilotDevice { capturable: Box, @@ -37,10 +37,10 @@ impl InputDevice for AutoPilotDevice { warn!("Failed to activate window, sending no input ({})", err); return; } - let (x_rel, y_rel, width_rel, height_rel) = match self.capturable.geometry_relative() { - Ok(g) => g, - Err(e) => { - warn!("Failed to get window geometry, sending no input ({})", e); + let (x_rel, y_rel, width_rel, height_rel) = match self.capturable.geometry().unwrap() { + Geometry::Relative(x, y, width, height) => (x, y, width, height), + _ => { + warn!("Failed to get window geometry, sending no input"); return; } }; diff --git a/src/input/autopilot_device_win.rs b/src/input/autopilot_device_win.rs new file mode 100644 index 00000000..8a2c79ff --- /dev/null +++ b/src/input/autopilot_device_win.rs @@ -0,0 +1,220 @@ +use winapi::shared::minwindef::DWORD; +use winapi::shared::windef::{HWND, POINT}; +use winapi::um::winuser::*; + +use tracing::warn; + +use crate::input::autopilot_device::AutoPilotDevice; +use crate::input::device::{InputDevice, InputDeviceType}; +use crate::protocol::{ + Button, KeyboardEvent, PointerEvent, PointerEventType, PointerType, WheelEvent, +}; + +use crate::capturable::{Capturable, Geometry}; + +pub struct WindowsInput { + capturable: Box, + autopilot_device: AutoPilotDevice, + pointer_device_handle: *mut HSYNTHETICPOINTERDEVICE__, + touch_device_handle: *mut HSYNTHETICPOINTERDEVICE__, + multitouch_map: std::collections::HashMap, +} + +impl WindowsInput { + pub fn new(capturable: Box) -> Self { + unsafe { + InitializeTouchInjection(5, TOUCH_FEEDBACK_DEFAULT); + Self { + capturable: capturable.clone(), + autopilot_device: AutoPilotDevice::new(capturable), + pointer_device_handle: CreateSyntheticPointerDevice(PT_PEN, 1, 1), + touch_device_handle: CreateSyntheticPointerDevice(PT_TOUCH, 5, 1), + multitouch_map: std::collections::HashMap::new(), + } + } + } +} + +impl InputDevice for WindowsInput { + fn send_wheel_event(&mut self, event: &WheelEvent) { + unsafe { mouse_event(MOUSEEVENTF_WHEEL, 0, 0, event.dy as DWORD, 0) }; + } + + fn send_pointer_event(&mut self, event: &PointerEvent) { + if let Err(err) = self.capturable.before_input() { + warn!("Failed to activate window, sending no input ({})", err); + return; + } + let (offset_x, offset_y, width, height, left, top) = + match self.capturable.geometry().unwrap() { + Geometry::VirtualScreen(offset_x, offset_y, width, height, left, top) => { + (offset_x, offset_y, width, height, left, top) + } + _ => unreachable!(), + }; + let (x, y) = ( + (event.x * width as f64) as i32 + offset_x, + (event.y * height as f64) as i32 + offset_y, + ); + let mut pointer_flags = match event.event_type { + PointerEventType::DOWN => { + POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN + } + PointerEventType::MOVE => POINTER_FLAG_INRANGE | POINTER_FLAG_UPDATE, + PointerEventType::UP => POINTER_FLAG_UP, + PointerEventType::CANCEL => { + POINTER_FLAG_INRANGE | POINTER_FLAG_UPDATE | POINTER_FLAG_CANCELED + } + }; + let button_change_type = match event.buttons { + Button::PRIMARY => { + pointer_flags |= POINTER_FLAG_INCONTACT; + POINTER_CHANGE_FIRSTBUTTON_DOWN + } + Button::SECONDARY => POINTER_CHANGE_SECONDBUTTON_DOWN, + Button::AUXILARY => POINTER_CHANGE_THIRDBUTTON_DOWN, + Button::NONE => POINTER_CHANGE_NONE, + _ => POINTER_CHANGE_NONE, + }; + if event.is_primary { + pointer_flags |= POINTER_FLAG_PRIMARY; + } + match event.pointer_type { + PointerType::Pen => { + unsafe { + let mut pointer_type_info = POINTER_TYPE_INFO { + type_: PT_PEN, + u: std::mem::zeroed(), + }; + *pointer_type_info.u.penInfo_mut() = POINTER_PEN_INFO { + pointerInfo: POINTER_INFO { + pointerType: PT_PEN, + pointerId: event.pointer_id as u32, + frameId: 0, + pointerFlags: pointer_flags, + sourceDevice: 0 as *mut winapi::ctypes::c_void, //maybe use syntheticPointerDeviceHandle here but works with 0 + hwndTarget: 0 as HWND, + ptPixelLocation: POINT { x: x, y: y }, + ptHimetricLocation: POINT { x: 0, y: 0 }, + ptPixelLocationRaw: POINT { x: x, y: y }, + ptHimetricLocationRaw: POINT { x: 0, y: 0 }, + dwTime: 0, + historyCount: 1, + InputData: 0, + dwKeyStates: 0, + PerformanceCount: 0, + ButtonChangeType: button_change_type, + }, + penFlags: PEN_FLAG_NONE, + penMask: PEN_MASK_PRESSURE + | PEN_MASK_ROTATION + | PEN_MASK_TILT_X + | PEN_MASK_TILT_Y, + pressure: (event.pressure * 1024f64) as u32, + rotation: event.twist as u32, + tiltX: event.tilt_x, + tiltY: event.tilt_y, + }; + InjectSyntheticPointerInput(self.pointer_device_handle, &pointer_type_info, 1); + } + } + PointerType::Touch => { + unsafe { + let mut pointer_type_info = POINTER_TYPE_INFO { + type_: PT_TOUCH, + u: std::mem::zeroed(), + }; + + let mut pointer_touch_info: POINTER_TOUCH_INFO = std::mem::zeroed(); + pointer_touch_info.pointerInfo = std::mem::zeroed(); + pointer_touch_info.pointerInfo.pointerType = PT_TOUCH; + pointer_touch_info.pointerInfo.pointerFlags = pointer_flags; + pointer_touch_info.pointerInfo.pointerId = event.pointer_id as u32; //event.pointer_id as u32; Using the actual pointer id causes errors in the touch injection + pointer_touch_info.pointerInfo.ptPixelLocation = POINT { x, y }; + pointer_touch_info.touchFlags = TOUCH_FLAG_NONE; + pointer_touch_info.touchMask = TOUCH_MASK_PRESSURE; + pointer_touch_info.pressure = (event.pressure * 1024f64) as u32; + + pointer_touch_info.pointerInfo.ButtonChangeType = button_change_type; + + *pointer_type_info.u.touchInfo_mut() = pointer_touch_info; + self.multitouch_map + .insert(event.pointer_id, pointer_type_info); + let len = self.multitouch_map.len(); + + let mut pointer_type_info_vec: Vec = Vec::new(); + for (_i, info) in self.multitouch_map.iter().enumerate() { + pointer_type_info_vec.push(*info.1); + } + let b: Box<[POINTER_TYPE_INFO]> = pointer_type_info_vec.into_boxed_slice(); + let m: *mut POINTER_TYPE_INFO = Box::into_raw(b) as _; + + InjectSyntheticPointerInput(self.touch_device_handle, m, len as u32); + + match event.event_type { + PointerEventType::DOWN | PointerEventType::MOVE => {} + + PointerEventType::UP | PointerEventType::CANCEL => { + self.multitouch_map.remove(&event.pointer_id); + } + } + } + } + PointerType::Mouse => { + let mut dw_flags = 0; + + let (screen_x, screen_y) = ( + (event.x * width as f64) as i32 + left, + (event.y * height as f64) as i32 + top, + ); + + match event.event_type { + PointerEventType::DOWN => match event.buttons { + Button::PRIMARY => { + dw_flags |= MOUSEEVENTF_LEFTDOWN; + } + Button::SECONDARY => { + dw_flags |= MOUSEEVENTF_RIGHTDOWN; + } + Button::AUXILARY => { + dw_flags |= MOUSEEVENTF_MIDDLEDOWN; + } + _ => {} + }, + PointerEventType::MOVE => { + unsafe { SetCursorPos(screen_x, screen_y) }; + } + PointerEventType::UP => match event.button { + Button::PRIMARY => { + dw_flags |= MOUSEEVENTF_LEFTUP; + } + Button::SECONDARY => { + dw_flags |= MOUSEEVENTF_RIGHTUP; + } + Button::AUXILARY => { + dw_flags |= MOUSEEVENTF_MIDDLEUP; + } + _ => {} + }, + PointerEventType::CANCEL => { + dw_flags |= MOUSEEVENTF_LEFTUP; + } + } + unsafe { mouse_event(dw_flags, 0 as u32, 0 as u32, 0, 0) }; + } + PointerType::Unknown => todo!(), + } + } + + fn send_keyboard_event(&mut self, event: &KeyboardEvent) { + self.autopilot_device.send_keyboard_event(event); + } + + fn set_capturable(&mut self, capturable: Box) { + self.capturable = capturable; + } + + fn device_type(&self) -> InputDeviceType { + InputDeviceType::WindowsInput + } +} diff --git a/src/input/device.rs b/src/input/device.rs index 50a15fe2..424eebdf 100644 --- a/src/input/device.rs +++ b/src/input/device.rs @@ -5,6 +5,7 @@ use crate::protocol::{KeyboardEvent, PointerEvent, WheelEvent}; pub enum InputDeviceType { AutoPilotDevice, UInputDevice, + WindowsInput, } pub trait InputDevice { diff --git a/src/input/mod.rs b/src/input/mod.rs index 2a3145d7..a50dc5c1 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,6 +1,8 @@ pub mod autopilot_device; pub mod device; +#[cfg(target_os = "windows")] +pub mod autopilot_device_win; #[cfg(target_os = "linux")] pub mod uinput_device; #[cfg(target_os = "linux")] diff --git a/src/input/uinput_device.rs b/src/input/uinput_device.rs index 52de374d..4a1ea9df 100644 --- a/src/input/uinput_device.rs +++ b/src/input/uinput_device.rs @@ -3,7 +3,7 @@ use std::ffi::CString; use std::os::raw::{c_char, c_int}; use crate::capturable::x11::X11Context; -use crate::capturable::Capturable; +use crate::capturable::{Capturable, Geometry}; use crate::input::device::{InputDevice, InputDeviceType}; use crate::protocol::{ Button, KeyboardEvent, KeyboardEventType, KeyboardLocation, PointerEvent, PointerEventType, @@ -34,6 +34,7 @@ pub struct UInputDevice { touch_fd: c_int, touches: [Option; 5], tool_pen_active: bool, + pen_touching: bool, capturable: Box, x: f64, y: f64, @@ -100,6 +101,7 @@ impl UInputDevice { touch_fd, touches: Default::default(), tool_pen_active: false, + pen_touching: false, capturable, x: 0.0, y: 0.0, @@ -262,9 +264,9 @@ impl InputDevice for UInputDevice { self.mouse_fd, ET_RELATIVE, EC_REL_HWHEEL, - direction(event.dy), + direction(event.dx), ); - self.send(self.mouse_fd, ET_RELATIVE, EC_REL_WHEEL_HI_RES, event.dx); + self.send(self.mouse_fd, ET_RELATIVE, EC_REL_WHEEL_HI_RES, event.dy); self.send(self.mouse_fd, ET_RELATIVE, EC_REL_HWHEEL_HI_RES, event.dx); self.send( @@ -281,10 +283,10 @@ impl InputDevice for UInputDevice { warn!("Failed to activate window, sending no input ({})", err); return; } - let (x, y, width, height) = match self.capturable.geometry_relative() { - Ok(g) => g, - Err(e) => { - warn!("Failed to get window geometry, sending no input ({})", e); + let (x, y, width, height) = match self.capturable.geometry().unwrap() { + Geometry::Relative(x, y, width, height) => (x, y, width, height), + _ => { + warn!("Failed to get window geometry, sending no input"); return; } }; @@ -445,6 +447,7 @@ impl InputDevice for UInputDevice { match event.event_type { PointerEventType::DOWN | PointerEventType::MOVE => { if let PointerEventType::DOWN = event.event_type { + self.pen_touching = true; self.send(self.stylus_fd, ET_KEY, EC_KEY_TOUCH, 1); } if !self.tool_pen_active && !event.buttons.contains(Button::ERASER) { @@ -473,7 +476,11 @@ impl InputDevice for UInputDevice { self.stylus_fd, ET_ABSOLUTE, EC_ABSOLUTE_PRESSURE, - self.transform_pressure(event.pressure), + if self.pen_touching { + self.transform_pressure(event.pressure) + } else { + 0 + }, ); self.send( self.stylus_fd, @@ -494,6 +501,7 @@ impl InputDevice for UInputDevice { self.send(self.stylus_fd, ET_KEY, EC_KEY_TOOL_RUBBER, 0); self.send(self.stylus_fd, ET_ABSOLUTE, EC_ABSOLUTE_PRESSURE, 0); self.tool_pen_active = false; + self.pen_touching = false; } } self.send( diff --git a/src/main.rs b/src/main.rs index c9b07696..4a04d6e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,16 @@ extern crate test; #[macro_use] extern crate bitflags; -use tracing::{error, warn}; +use clap::CommandFactory; +use clap_complete::generate; +#[cfg(unix)] +use signal_hook::iterator::Signals; +use signal_hook::{consts::TERM_SIGNALS, low_level::signal_name}; +use tracing::{error, info, warn}; use std::sync::mpsc; use config::{get_config, Config}; -use structopt::StructOpt; mod capturable; mod cerror; @@ -32,7 +36,12 @@ fn main() { let conf = get_config(); if let Some(shell) = conf.completions { - Config::clap().gen_completions_to("weylus", shell, &mut std::io::stdout()); + generate( + shell, + &mut Config::command(), + "weylus", + &mut std::io::stdout(), + ); return; } @@ -67,20 +76,43 @@ fn main() { } } - if !conf.no_gui { - gui::run(&conf, receiver); - } else { + if conf.no_gui { let mut weylus = crate::weylus::Weylus::new(); - weylus.start( - &conf, - |_| {}, - |msg| { - if let crate::websocket::Ws2UiMessage::UInputInaccessible = msg { - warn!(std::include_str!("strings/uinput_error.txt")); - } - }, - ); - weylus.wait(); + weylus.start(&conf, |msg| { + if let crate::web::Web2UiMessage::UInputInaccessible = msg { + warn!(std::include_str!("strings/uinput_error.txt")); + } + }); + #[cfg(unix)] + { + let mut signals = Signals::new(TERM_SIGNALS).unwrap(); + for sig in signals.forever() { + info!( + "Shutting down after receiving signal {signame} ({sig})...", + signame = signal_name(sig).unwrap_or("UNKNOWN SIGNAL") + ); + std::thread::spawn(move || { + for sig in signals.forever() { + warn!( + "Received second signal {signame} ({sig}) while shutting down \ + gracefully, proceeding with forceful shutdown...", + signame = signal_name(sig).unwrap_or("UNKNOWN SIGNAL") + ); + std::process::exit(1); + } + }); + weylus.stop(); + break; + } + } + #[cfg(not(unix))] + { + loop { + std::thread::park(); + } + } + } else { + gui::run(&conf, receiver); } } diff --git a/src/protocol.rs b/src/protocol.rs index d15e130c..ecf452aa 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -9,6 +9,7 @@ pub struct ClientConfiguration { pub max_width: usize, pub max_height: usize, pub client_name: Option, + pub frame_rate: f64, } #[derive(Serialize, Deserialize, Debug)] @@ -16,10 +17,6 @@ pub enum MessageInbound { PointerEvent(PointerEvent), WheelEvent(WheelEvent), KeyboardEvent(KeyboardEvent), - // request a video frame from the server - // like this the client can partially control the framerate by sending requests at some given - // rate. However, the server may drop a request if encoding is too slow. - TryGetFrame, GetCapturableList, Config(ClientConfiguration), } @@ -89,7 +86,7 @@ fn location_from<'de, D: Deserializer<'de>>(deserializer: D) -> Result Result<(), Self::Error>; + fn send_video(&mut self, bytes: &[u8]) -> Result<(), Self::Error>; +} + +pub trait WeylusReceiver: Iterator> { + type Error: std::error::Error; + fn recv_message(&mut self) -> Option> { + self.next() + } +} diff --git a/src/video.rs b/src/video.rs index 731812f4..984ded95 100644 --- a/src/video.rs +++ b/src/video.rs @@ -70,7 +70,7 @@ pub struct VideoEncoder { height_in: usize, width_out: usize, height_out: usize, - write_data: Box, + write_data: Box, start_time: Instant, } @@ -80,7 +80,7 @@ impl VideoEncoder { height_in: usize, width_out: usize, height_out: usize, - write_data: impl Fn(&[u8]) + 'static, + mut write_data: impl FnMut(&[u8]) + 'static, options: EncoderOptions, ) -> Result, CError> { let mut video_encoder = Box::new(Self { diff --git a/src/web.rs b/src/web.rs index c343f4ae..5fef7eb7 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,30 +1,51 @@ +use bytes::Bytes; +use fastwebsockets::upgrade; use handlebars::Handlebars; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{server::conn::AddrStream, Body, Method, Request, Response, Server, StatusCode}; +use http_body_util::combinators::BoxBody; +use http_body_util::{BodyExt, Full}; +use hyper::body::Incoming; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{Method, Request, Response, StatusCode}; +use hyper_util::rt::TokioIo; use serde::Serialize; use std::collections::HashMap; +use std::convert::Infallible; use std::net::SocketAddr; -use std::sync::mpsc; -use std::sync::mpsc::SendError; +use std::path::PathBuf; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use tokio::sync::mpsc as mpsc_tokio; +use std::time::Duration; +use tokio::net::TcpListener; +use tokio::sync::{mpsc, oneshot}; use tracing::{debug, error, info, warn}; +use crate::websocket::{weylus_websocket_channel, WeylusClientConfig, WeylusClientHandler}; + +#[derive(Debug)] +pub enum WebStartUpMessage { + Start, + Error, +} + +pub enum Web2UiMessage { + UInputInaccessible, +} + pub const INDEX_HTML: &str = std::include_str!("../www/templates/index.html"); pub const ACCESS_HTML: &str = std::include_str!("../www/static/access_code.html"); pub const STYLE_CSS: &str = std::include_str!("../www/static/style.css"); pub const LIB_JS: &str = std::include_str!("../www/static/lib.js"); #[derive(Serialize)] -struct WebConfig { +struct IndexTemplateContext { access_code: Option, - websocket_port: u16, uinput_enabled: bool, capture_cursor_enabled: bool, log_level: String, } -fn response_from_str(s: &str, content_type: &str) -> Response { +fn response_from_str(s: &str, content_type: &str) -> Response> { Response::builder() .status(StatusCode::OK) .header("content-type", content_type) @@ -32,7 +53,7 @@ fn response_from_str(s: &str, content_type: &str) -> Response { .unwrap() } -fn response_not_found() -> Response { +fn response_not_found() -> Response> { Response::builder() .status(StatusCode::NOT_FOUND) .header("content-type", "text/html; charset=utf-8") @@ -40,13 +61,13 @@ fn response_not_found() -> Response { .unwrap() } -fn response_from_path_or_default( - path: Option<&String>, +async fn response_from_path_or_default( + path: Option<&PathBuf>, default: &str, content_type: &str, -) -> Response { +) -> Response> { match path { - Some(path) => match std::fs::read_to_string(path) { + Some(path) => match tokio::fs::read_to_string(path).await { Ok(s) => response_from_str(&s, content_type), Err(err) => { warn!("Failed to load file: {}", err); @@ -59,14 +80,17 @@ fn response_from_path_or_default( async fn serve( addr: SocketAddr, - req: Request, + mut req: Request, context: Arc>, - _sender: mpsc::Sender, -) -> Result, hyper::Error> { + sender_ui: mpsc::Sender, + num_clients: Arc, + semaphore_websocket_shutdown: Arc, + notify_disconnect: Arc, +) -> Result>, hyper::Error> { debug!("Got request: {:?}", req); let mut authed = false; - if let Some(access_code) = &context.access_code { - if req.method() == Method::GET && req.uri().path() == "/" { + if let Some(access_code) = &context.web_config.access_code { + if req.method() == Method::GET && (req.uri().path() == "/" || req.uri().path() == "/ws") { use url::form_urlencoded; if let Some(query) = req.uri().query() { let params = form_urlencoded::parse(query.as_bytes()) @@ -84,27 +108,27 @@ async fn serve( authed = true; } if req.method() != Method::GET { - return Ok(response_not_found()); + return Ok(response_not_found().map(|r| r.boxed())); } match req.uri().path() { "/" => { if !authed { return Ok(response_from_path_or_default( - context.custom_access_html.as_ref(), + context.web_config.custom_access_html.as_ref(), ACCESS_HTML, "text/html; charset=utf-8", - )); + ) + .await + .map(|r| r.boxed())); } - debug!(address = ?addr, "Client connected."); - let config = WebConfig { - access_code: context.access_code.clone(), - websocket_port: context.ws_port, + let config = IndexTemplateContext { + access_code: context.web_config.access_code.clone(), uinput_enabled: cfg!(target_os = "linux"), capture_cursor_enabled: cfg!(not(target_os = "windows")), log_level: crate::log::get_log_level().to_string(), }; - let html = if let Some(path) = context.custom_index_html.as_ref() { + let html = if let Some(path) = context.web_config.custom_index_html.as_ref() { let mut reg = Handlebars::new(); if let Err(err) = reg.register_template_file("index", path) { warn!("Failed to register template from path: {}", err); @@ -117,63 +141,102 @@ async fn serve( }; match html { - Ok(html) => Ok(response_from_str(&html, "text/html; charset=utf-8")), + Ok(html) => { + Ok(response_from_str(&html, "text/html; charset=utf-8").map(|r| r.boxed())) + } Err(err) => { error!("Failed to render index template: {}", err); - Ok(response_not_found()) + Ok(response_not_found().map(|r| r.boxed())) } } } + "/ws" => { + if !authed { + return Ok(Response::builder() + .status(StatusCode::UNAUTHORIZED) + .body("unauthorized".to_string().boxed()) + .unwrap()); + } + + let (response, fut) = upgrade::upgrade(&mut req).unwrap(); + num_clients.fetch_add(1, Ordering::Relaxed); + + let config = context.weylus_client_config.clone(); + tokio::spawn(async move { + match fut.await { + Ok(ws) => { + let (sender, receiver) = + weylus_websocket_channel(ws, semaphore_websocket_shutdown); + std::thread::spawn(move || { + let client = WeylusClientHandler::new( + sender, + receiver, + || { + if let Err(err) = + sender_ui.blocking_send(Web2UiMessage::UInputInaccessible) + { + warn!( + "Failed to send message 'UInputInaccessible': {err}." + ); + } + }, + config, + ); + client.run(); + num_clients.fetch_sub(1, Ordering::Relaxed); + notify_disconnect.notify_waiters(); + }); + } + Err(err) => { + eprintln!("Error in websocket connection: {}", err); + num_clients.fetch_sub(1, Ordering::Relaxed); + notify_disconnect.notify_waiters(); + } + } + }); + + Ok(response.map(|r| r.boxed())) + } "/style.css" => Ok(response_from_path_or_default( - context.custom_style_css.as_ref(), + context.web_config.custom_style_css.as_ref(), STYLE_CSS, "text/css; charset=utf-8", - )), + ) + .await + .map(|r| r.boxed())), "/lib.js" => Ok(response_from_path_or_default( - context.custom_lib_js.as_ref(), + context.web_config.custom_lib_js.as_ref(), LIB_JS, "text/javascript; charset=utf-8", - )), - _ => Ok(response_not_found()), + ) + .await + .map(|r| r.boxed())), + _ => Ok(response_not_found().map(|r| r.boxed())), } } -#[derive(Debug)] -pub enum Ui2WebMessage { - Shutdown, -} -pub enum Web2UiMessage { - Start, - Error(String), -} - -fn log_send_error(res: Result<(), SendError>) { - if let Err(err) = res { - warn!("Webserver: Failed to send message to gui: {}", err); - } +#[derive(Clone)] +pub struct WebServerConfig { + pub bind_addr: SocketAddr, + pub access_code: Option, + pub custom_index_html: Option, + pub custom_access_html: Option, + pub custom_style_css: Option, + pub custom_lib_js: Option, } struct Context<'a> { - bind_addr: SocketAddr, - ws_port: u16, - access_code: Option, - custom_index_html: Option, - custom_access_html: Option, - custom_style_css: Option, - custom_lib_js: Option, + web_config: WebServerConfig, + weylus_client_config: WeylusClientConfig, templates: Handlebars<'a>, } pub fn run( - sender: mpsc::Sender, - receiver: mpsc_tokio::Receiver, - bind_addr: SocketAddr, - ws_port: u16, - access_code: Option, - custom_index_html: Option, - custom_access_html: Option, - custom_style_css: Option, - custom_lib_js: Option, + sender_ui: tokio::sync::mpsc::Sender, + sender_startup: oneshot::Sender, + notify_shutdown: Arc, + web_server_config: WebServerConfig, + weylus_client_config: WeylusClientConfig, ) -> std::thread::JoinHandle<()> { let mut templates = Handlebars::new(); templates @@ -181,61 +244,122 @@ pub fn run( .unwrap(); let context = Context { - bind_addr, - ws_port, - access_code, - custom_index_html, - custom_access_html, - custom_style_css, - custom_lib_js, + web_config: web_server_config, + weylus_client_config, templates, }; - std::thread::spawn(move || run_server(context, sender, receiver)) + std::thread::spawn(move || run_server(context, sender_ui, sender_startup, notify_shutdown)) } #[tokio::main] async fn run_server( context: Context<'static>, - sender: mpsc::Sender, - mut receiver: mpsc_tokio::Receiver, + sender_ui: tokio::sync::mpsc::Sender, + sender_startup: oneshot::Sender, + notify_shutdown: Arc, ) { - let addr = context.bind_addr; - let context = Arc::new(context); + let addr = context.web_config.bind_addr; - let sender = sender.clone(); - let sender2 = sender.clone(); - let service = make_service_fn(move |s: &AddrStream| { - let addr = s.remote_addr(); - let context = context.clone(); - let sender = sender.clone(); - async move { - Ok::<_, hyper::Error>(service_fn(move |req| { - let context = context.clone(); - serve(addr, req, context, sender.clone()) - })) - } - }); - let server = match Server::try_bind(&addr) { - Ok(builder) => builder.serve(service), + let listener = match TcpListener::bind(addr).await { + Ok(listener) => listener, Err(err) => { - log_send_error(sender2.send(Web2UiMessage::Error(format!( - "Failed to start webserver: {}", - err - )))); + error!("Failed to bind to socket: {err}."); + sender_startup.send(WebStartUpMessage::Error).unwrap(); return; } }; - let server = server.with_graceful_shutdown(async move { - loop { - match receiver.recv().await { - Some(Ui2WebMessage::Shutdown) => break, - None => break, + + sender_startup.send(WebStartUpMessage::Start).unwrap(); + + let context = Arc::new(context); + + let broadcast_shutdown = Arc::new(tokio::sync::Notify::new()); + + let num_clients = Arc::new(AtomicUsize::new(0)); + let notify_disconnect = Arc::new(tokio::sync::Notify::new()); + let semaphore_websocket_shutdown = Arc::new(tokio::sync::Semaphore::new(0)); + + loop { + let (tcp, remote_address) = tokio::select! { + res = listener.accept() => { + match res { + Ok(conn) => conn, + Err(err) => { + warn!("Connection failed: {err}."); + continue; + } + } + }, + _ = notify_shutdown.notified() => { + info!("Webserver is shutting down."); + broadcast_shutdown.notify_waiters(); + break; } + }; + + debug!(address = ?remote_address, "Client connected."); + + let io = TokioIo::new(tcp); + + let sender_ui = sender_ui.clone(); + let broadcast_shutdown = broadcast_shutdown.clone(); + let context = context.clone(); + let num_clients = num_clients.clone(); + let semaphore_websocket_shutdown = semaphore_websocket_shutdown.clone(); + let notify_disconnect = notify_disconnect.clone(); + + tokio::task::spawn(async move { + let conn = http1::Builder::new().serve_connection( + io, + service_fn({ + move |req| { + let context = context.clone(); + let num_clients = num_clients.clone(); + let semaphore_websocket_shutdown = semaphore_websocket_shutdown.clone(); + let notify_disconnect = notify_disconnect.clone(); + serve( + remote_address, + req, + context, + sender_ui.clone(), + num_clients, + semaphore_websocket_shutdown, + notify_disconnect, + ) + } + }), + ); + + let conn = conn.with_upgrades(); + + tokio::select! { + conn = conn => match conn { + Ok(_) => (), + Err(err) => { + warn!("Error polling connection ({remote_address}): {err}.") + } + }, + _ = broadcast_shutdown.notified() => { + info!("Closing connection to: {remote_address}."); + } + } + }); + } + + semaphore_websocket_shutdown.add_permits(num_clients.load(Ordering::Relaxed)); + + loop { + let remaining_clients = num_clients.load(Ordering::Relaxed); + if remaining_clients == 0 { + break; + } else { + debug!("Waiting for remaining clients ({remaining_clients}) to disconnect."); } - }); - info!("Webserver listening at {}...", addr); - log_send_error(sender2.send(Web2UiMessage::Start)); - if let Err(err) = server.await { - error!("Webserver exited error: {}", err) - }; + tokio::select! { + _ = notify_disconnect.notified() => (), + _ = tokio::time::sleep(Duration::from_secs(1)) => { + semaphore_websocket_shutdown.add_permits(num_clients.load(Ordering::Relaxed)); + }, + } + } } diff --git a/src/websocket.rs b/src/websocket.rs index 3fd7e1d7..4fc470ea 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -1,405 +1,135 @@ -use std::collections::HashMap; -use std::net::{SocketAddr, TcpStream}; -use std::sync::mpsc::{SendError, TryRecvError}; -use std::sync::{mpsc, Arc, Mutex}; -use std::thread::spawn; -use tracing::{debug, error, info, warn}; - -use websocket::sender::Writer; -use websocket::server::upgrade::{sync::Buffer as WsBuffer, WsUpgrade}; -use websocket::sync::Server; -use websocket::{Message, OwnedMessage, WebSocketError}; +use fastwebsockets::{FragmentCollectorRead, Frame, OpCode, WebSocket, WebSocketError}; +use hyper::upgrade::Upgraded; +use hyper_util::rt::TokioIo; +use std::convert::Infallible; +use std::sync::mpsc::{RecvTimeoutError, SendError}; +use std::sync::{mpsc, Arc}; +use std::thread::{spawn, JoinHandle}; +use std::time::{Duration, Instant}; +use tokio::sync::mpsc::channel; +use tracing::{debug, error, warn}; use crate::capturable::{get_capturables, Capturable, Recorder}; use crate::input::device::{InputDevice, InputDeviceType}; use crate::protocol::{ - ClientConfiguration, KeyboardEvent, MessageInbound, MessageOutbound, PointerEvent, WheelEvent, + ClientConfiguration, KeyboardEvent, MessageInbound, MessageOutbound, PointerEvent, + WeylusReceiver, WeylusSender, WheelEvent, }; use crate::cerror::CErrorCode; use crate::video::{EncoderOptions, VideoEncoder}; -type WsWriter = Arc>>; -type WsClients = Arc>>; - -pub enum Ws2UiMessage { - Start, - UInputInaccessible, - Error(String), -} - -pub enum Ui2WsMessage { - Shutdown, -} - -#[derive(Clone)] -pub struct WsConfig { - pub address: SocketAddr, - pub access_code: Option, - pub encoder_options: EncoderOptions, - #[cfg(target_os = "linux")] - pub wayland_support: bool, -} - -fn log_send_error(res: Result<(), SendError>) { - if let Err(err) = res { - warn!("Websocket: Failed to send message to ui: {}", err); - } -} - -pub fn run( - sender: mpsc::Sender, - receiver: mpsc::Receiver, - config: WsConfig, -) -> std::thread::JoinHandle<()> { - spawn(move || { - let clients: WsClients = Arc::new(Mutex::new(HashMap::new())); - - let mut server = match Server::bind(config.address) { - Ok(s) => s, - Err(e) => { - log_send_error(sender.send(Ws2UiMessage::Error(format!( - "Failed binding to socket: {}", - e - )))); - return; - } - }; - - if let Err(err) = server.set_nonblocking(true) { - warn!( - "Could not set websocket to non-blocking, graceful shutdown may be impossible now: {}", - err - ); - } - - log_send_error(sender.send(Ws2UiMessage::Start)); - - loop { - std::thread::sleep(std::time::Duration::from_millis(10)); - - match receiver.try_recv() { - Err(TryRecvError::Disconnected) | Ok(Ui2WsMessage::Shutdown) => { - let clients = clients.lock().unwrap(); - for client in clients.values() { - let client = client.lock().unwrap(); - if let Err(err) = client.shutdown_all() { - error!("Could not shutdown websocket client: {}", err); - } - } - info!("Shutting down websocket: {}", config.address); - return; - } - _ => {} - } - match server.accept() { - Ok(request) => { - let clients = clients.clone(); - let config = config.clone(); - let sender = sender.clone(); - spawn(move || handle_connection(request, clients, config, sender)); - } - _ => {} - }; - } - }) -} - -fn handle_connection( - request: WsUpgrade>, - clients: WsClients, - config: WsConfig, - gui_sender: mpsc::Sender, -) { - let client = match request.accept() { - Ok(c) => c, - Err((_, err)) => { - warn!("Failed to accept client: {}", err); - return; - } - }; - - if let Err(err) = client.set_nonblocking(false) { - warn!("Failed to set client to blocking mode: {}", err); - } - - let peer_addr = match client.peer_addr() { - Ok(p) => p, - Err(err) => { - warn!("Failed to retrieve client address: {}", err); - return; - } - }; - - let (mut ws_receiver, ws_sender) = match client.split() { - Ok(s) => s, - Err(err) => { - warn!("Failed to setup connection: {}", err); - return; - } - }; - - let ws_sender = Arc::new(Mutex::new(ws_sender)); - - { - let mut clients = clients.lock().unwrap(); - clients.insert(peer_addr, ws_sender.clone()); - info!(address = ?peer_addr, "Client connected."); - } - - let mut ws_handler = WsHandler::new(ws_sender, config.clone(), gui_sender, peer_addr); - - let mut authed = config.access_code.is_none(); - let access_code = config.access_code.unwrap_or_else(|| "".into()); - for msg in ws_receiver.incoming_messages() { - match msg { - Ok(msg) => { - if !authed { - if let OwnedMessage::Text(pw) = &msg { - if pw == &access_code { - authed = true; - info!(address = ?peer_addr, "WS-Client authenticated!"); - } else { - warn!( - address = ?peer_addr, - access_code = %pw, - "Authentication failed, wrong access code", - ); - let mut clients = clients.lock().unwrap(); - clients.remove(&peer_addr); - return; - } - } - } else { - ws_handler.process(&msg); - } - if msg.is_close() { - let mut clients = clients.lock().unwrap(); - info!(address = ?peer_addr, "Client disconnected."); - clients.remove(&peer_addr); - return; - } - } - Err(err) => { - match err { - // this happens on calling shutdown, no need to log this - WebSocketError::NoDataAvailable => (), - _ => warn!("Error reading message from websocket, closing ({})", err), - } - - let mut clients = clients.lock().unwrap(); - clients.remove(&peer_addr); - return; - } - } - } -} - -fn send_msg(sender: &WsWriter, msg: &MessageOutbound) { - if let Err(err) = sender - .lock() - .unwrap() - .send_message(&Message::text(serde_json::to_string(msg).unwrap())) - { - warn!("Failed to send message to websocket: {}", err); - } -} - struct VideoConfig { capturable: Box, capture_cursor: bool, max_width: usize, max_height: usize, + frame_rate: f64, } enum VideoCommands { Start(VideoConfig), - TryGetFrame, } -fn handle_video(receiver: mpsc::Receiver, sender: WsWriter, config: WsConfig) { - let mut recorder: Option> = None; - let mut video_encoder: Option> = None; - - let mut max_width = 1920; - let mut max_height = 1080; - - loop { - // stop thread once the channel is closed - let mut msg = match receiver.recv() { - Ok(m) => m, - Err(_) => return, - }; - - // drop frames if the client is requesting frames at a higher rate than they can be - // produced here - if let VideoCommands::TryGetFrame = msg { - loop { - match receiver.try_recv() { - Err(mpsc::TryRecvError::Empty) => break, - Err(mpsc::TryRecvError::Disconnected) => return, - Ok(VideoCommands::TryGetFrame) => continue, - Ok(tmp_msg) => { - msg = tmp_msg; - break; - } - } - } - } - match msg { - VideoCommands::TryGetFrame => { - if recorder.is_none() { - warn!("Screen capture not initalized, can not send video frame!"); - continue; - } - let pixel_data = recorder.as_mut().unwrap().capture(); - if let Err(err) = pixel_data { - warn!("Error capturing screen: {}", err); - continue; - } - let pixel_data = pixel_data.unwrap(); - let (width_in, height_in) = pixel_data.size(); - let scale = - (max_width as f64 / width_in as f64).min(max_height as f64 / height_in as f64); - // limit video to 4K - let scale_max = (3840.0 / width_in as f64).min(2160.0 / height_in as f64); - let scale = scale.min(scale_max); - let mut width_out = width_in; - let mut height_out = height_in; - if scale < 1.0 { - width_out = (width_out as f64 * scale) as usize; - height_out = (height_out as f64 * scale) as usize; - } - // video encoder is not setup or setup for encoding the wrong size: restart it - if video_encoder.is_none() - || !video_encoder - .as_ref() - .unwrap() - .check_size(width_in, height_in, width_out, height_out) - { - send_msg(&sender, &MessageOutbound::NewVideo); - let sender = sender.clone(); - let res = VideoEncoder::new( - width_in, - height_in, - width_out, - height_out, - move |data| { - let msg = Message::binary(data); - if let Err(err) = sender.lock().unwrap().send_message(&msg) { - match err { - WebSocketError::IoError(err) => { - // ignore broken pipe errors as those are caused by - // intentionally shutting down the websocket - if err.kind() == std::io::ErrorKind::BrokenPipe { - debug!("Error sending video: {}", err); - } else { - warn!("Error sending video: {}", err); - } - } - _ => warn!("Error sending video: {}", err), - } - } - }, - config.encoder_options, - ); - match res { - Ok(r) => video_encoder = Some(r), - Err(e) => { - warn!("{}", e); - continue; - } - }; - } - let video_encoder = video_encoder.as_mut().unwrap(); - video_encoder.encode(pixel_data); - } - VideoCommands::Start(config) => { - #[allow(unused_assignments)] - { - // gstpipewire can not handle setting a pipeline's state to Null after another - // pipeline has been created and its state has been set to Play. - // This line makes sure that there always is only a single recorder and thus - // single pipeline in this thread by forcing rust to call the destructor of the - // current pipeline here, right before creating a new pipeline. - // See: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/986 - // - // This shouldn't affect other Recorder trait objects. - recorder = None; - } - match config.capturable.recorder(config.capture_cursor) { - Ok(r) => { - recorder = Some(r); - max_width = config.max_width; - max_height = config.max_height; - send_msg(&sender, &MessageOutbound::ConfigOk); - } - Err(err) => { - warn!("Failed to init screen cast: {}!", err); - send_msg( - &sender, - &MessageOutbound::Error("Failed to init screen cast!".into()), - ) - } - } - } - } +fn send_message(sender: &mut S, message: MessageOutbound) +where + S: WeylusSender, +{ + if let Err(err) = sender.send_message(message) { + warn!("Failed to send message to client: {err}"); } } -struct WsHandler { - sender: WsWriter, +pub struct WeylusClientHandler { + sender: S, + receiver: Option, video_sender: mpsc::Sender, input_device: Option>, capturables: Vec>, - gui_sender: mpsc::Sender, - ws_config: WsConfig, + on_uinput_inaccessible: FnUInput, + config: WeylusClientConfig, #[cfg(target_os = "linux")] capture_cursor: bool, client_name: Option, - client_address: SocketAddr, + video_thread: JoinHandle<()>, } -impl WsHandler { - fn new( - sender: WsWriter, - config: WsConfig, - gui_sender: mpsc::Sender, - client_address: SocketAddr, - ) -> Self { +#[derive(Clone, Copy)] +pub struct WeylusClientConfig { + pub encoder_options: EncoderOptions, + #[cfg(target_os = "linux")] + pub wayland_support: bool, +} + +impl WeylusClientHandler { + pub fn new( + sender: S, + receiver: R, + on_uinput_inaccessible: FnUInput, + config: WeylusClientConfig, + ) -> Self + where + R: WeylusReceiver, + S: WeylusSender + Clone + Send + Sync + 'static, + { let (video_sender, video_receiver) = mpsc::channel::(); - { + let video_thread = { let sender = sender.clone(); - let config = config.clone(); // offload creating the videostream to another thread to avoid blocking the thread that // is receiving messages from the websocket - spawn(move || handle_video(video_receiver, sender, config)); - } + spawn(move || handle_video(video_receiver, sender, config.encoder_options)) + }; Self { sender, + receiver: Some(receiver), video_sender, input_device: None, capturables: vec![], - gui_sender, - ws_config: config, + on_uinput_inaccessible, + config, #[cfg(target_os = "linux")] capture_cursor: false, client_name: None, - client_address, + video_thread, } } - fn send_msg(&self, msg: &MessageOutbound) { - send_msg(&self.sender, msg) + pub fn run(mut self) + where + R: WeylusReceiver, + S: WeylusSender + Clone + Send + Sync + 'static, + FnUInput: Fn(), + { + for message in self.receiver.take().unwrap() { + match message { + Ok(message) => match message { + MessageInbound::PointerEvent(event) => self.process_pointer_event(&event), + MessageInbound::WheelEvent(event) => self.process_wheel_event(&event), + MessageInbound::KeyboardEvent(event) => self.process_keyboard_event(&event), + MessageInbound::GetCapturableList => self.send_capturable_list(), + MessageInbound::Config(config) => self.update_config(config), + }, + Err(err) => { + warn!("Failed to read message {err}!"); + self.send_message(MessageOutbound::Error( + "Failed to read message!".to_string(), + )); + } + } + } + + drop(self.video_sender); + if let Err(err) = self.video_thread.join() { + warn!("Failed to join video thread: {err:?}"); + } } - // Enqueue a request to send a new video frame. - // - // This does not do any further work in order not to block receiving messages. `handle_video` - // is resposible to do the actual work. - fn queue_try_send_video_frame(&mut self) { - self.video_sender.send(VideoCommands::TryGetFrame).unwrap(); + fn send_message(&mut self, message: MessageOutbound) + where + S: WeylusSender, + { + send_message(&mut self.sender, message) } fn process_wheel_event(&mut self, event: &WheelEvent) { @@ -431,21 +161,28 @@ impl WsHandler { } } - fn send_capturable_list(&mut self) { + fn send_capturable_list(&mut self) + where + S: WeylusSender, + { let mut windows = Vec::::new(); self.capturables = get_capturables( #[cfg(target_os = "linux")] - self.ws_config.wayland_support, + self.config.wayland_support, #[cfg(target_os = "linux")] self.capture_cursor, ); self.capturables.iter().for_each(|c| { windows.push(c.name()); }); - self.send_msg(&MessageOutbound::CapturableList(windows)); + self.send_message(MessageOutbound::CapturableList(windows)); } - fn setup(&mut self, config: ClientConfiguration) { + fn update_config(&mut self, config: ClientConfiguration) + where + S: WeylusSender, + FnUInput: Fn(), + { let client_name_changed = if self.client_name != config.client_name { self.client_name = config.client_name; true @@ -474,13 +211,9 @@ impl WsHandler { Err(e) => { error!("Failed to create uinput device: {}", e); if let CErrorCode::UInputNotAccessible = e.to_enum() { - if let Err(err) = - self.gui_sender.send(Ws2UiMessage::UInputInaccessible) - { - warn!("Failed to send message to gui thread: {}!", err); - } + (self.on_uinput_inaccessible)(); } - self.send_msg(&MessageOutbound::ConfigError( + self.send_message(MessageOutbound::ConfigError( "Failed to create uinput device!".to_string(), )); return; @@ -499,7 +232,7 @@ impl WsHandler { d.set_capturable(capturable.clone()); } - #[cfg(not(target_os = "linux"))] + #[cfg(target_os = "macos")] if self.input_device.is_none() { self.input_device = Some(Box::new( crate::input::autopilot_device::AutoPilotDevice::new(capturable.clone()), @@ -509,6 +242,16 @@ impl WsHandler { .as_mut() .map(|d| d.set_capturable(capturable.clone())); } + #[cfg(target_os = "windows")] + if self.input_device.is_none() { + self.input_device = Some(Box::new( + crate::input::autopilot_device_win::WindowsInput::new(capturable.clone()), + )); + } else { + self.input_device + .as_mut() + .map(|d| d.set_capturable(capturable.clone())); + } self.video_sender .send(VideoCommands::Start(VideoConfig { @@ -516,54 +259,279 @@ impl WsHandler { capture_cursor: config.capture_cursor, max_width: config.max_width, max_height: config.max_height, + frame_rate: config.frame_rate, })) .unwrap(); } else { error!("Got invalid id for capturable: {}", config.capturable_id); - self.send_msg(&MessageOutbound::ConfigError( + self.send_message(MessageOutbound::ConfigError( "Invalid id for capturable!".to_string(), )); } } +} - fn process(&mut self, message: &OwnedMessage) { - match message { - OwnedMessage::Text(s) => { - let message: Result = serde_json::from_str(s); - match message { - Ok(message) => { - if let MessageInbound::TryGetFrame = message { - } else { - debug!( - client_message = %s, - address = ?self.client_address, - "Got message from client." - ); - } - match message { - MessageInbound::WheelEvent(event) => { - self.process_wheel_event(&event); - } - MessageInbound::PointerEvent(event) => { - self.process_pointer_event(&event); +fn handle_video( + receiver: mpsc::Receiver, + mut sender: S, + encoder_options: EncoderOptions, +) { + const EFFECTIVE_INIFINIT: Duration = Duration::from_secs(3600 * 24 * 365 * 200); + + let mut recorder: Option> = None; + let mut video_encoder: Option> = None; + + let mut max_width = 1920; + let mut max_height = 1080; + let mut frame_duration = EFFECTIVE_INIFINIT; + let mut last_frame = Instant::now(); + + loop { + let now = Instant::now(); + let elapsed = now - last_frame; + let frames_passed = (elapsed.as_secs_f64() / frame_duration.as_secs_f64()) as u32; + let next_frame = last_frame + (frames_passed + 1) * frame_duration; + let timeout = next_frame - now; + last_frame = next_frame; + + if frames_passed > 0 { + debug!("Dropped {frames_passed} frame(s)!"); + } + + match receiver.recv_timeout(timeout) { + Ok(VideoCommands::Start(config)) => { + #[allow(unused_assignments)] + { + // gstpipewire can not handle setting a pipeline's state to Null after another + // pipeline has been created and its state has been set to Play. + // This line makes sure that there always is only a single recorder and thus + // single pipeline in this thread by forcing rust to call the destructor of the + // current pipeline here, right before creating a new pipeline. + // See: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/986 + // + // This shouldn't affect other Recorder trait objects. + recorder = None; + } + match config.capturable.recorder(config.capture_cursor) { + Ok(r) => { + recorder = Some(r); + max_width = config.max_width; + max_height = config.max_height; + send_message(&mut sender, MessageOutbound::ConfigOk); + } + Err(err) => { + warn!("Failed to init screen cast: {}!", err); + send_message( + &mut sender, + MessageOutbound::Error("Failed to init screen cast!".into()), + ) + } + } + last_frame = Instant::now(); + + // The Duration type can not handle infinity, if the frame rate is set to 0 we just + // set the duration between two frames to a very long one, which is effectively + // infinity. + let d = 1.0 / config.frame_rate; + frame_duration = if d.is_finite() { + Duration::from_secs_f64(d) + } else { + EFFECTIVE_INIFINIT + }; + frame_duration = frame_duration.min(EFFECTIVE_INIFINIT); + } + Err(RecvTimeoutError::Timeout) => { + if recorder.is_none() { + warn!("Screen capture not initalized, can not send video frame!"); + continue; + } + let pixel_data = recorder.as_mut().unwrap().capture(); + if let Err(err) = pixel_data { + warn!("Error capturing screen: {}", err); + continue; + } + let pixel_data = pixel_data.unwrap(); + let (width_in, height_in) = pixel_data.size(); + let scale = + (max_width as f64 / width_in as f64).min(max_height as f64 / height_in as f64); + // limit video to 4K + let scale_max = (3840.0 / width_in as f64).min(2160.0 / height_in as f64); + let scale = scale.min(scale_max); + let mut width_out = width_in; + let mut height_out = height_in; + if scale < 1.0 { + width_out = (width_out as f64 * scale) as usize; + height_out = (height_out as f64 * scale) as usize; + } + // video encoder is not setup or setup for encoding the wrong size: restart it + if video_encoder.is_none() + || !video_encoder + .as_ref() + .unwrap() + .check_size(width_in, height_in, width_out, height_out) + { + send_message(&mut sender, MessageOutbound::NewVideo); + let mut sender = sender.clone(); + let res = VideoEncoder::new( + width_in, + height_in, + width_out, + height_out, + move |data| { + if let Err(err) = sender.send_video(data) { + warn!("Failed to send video frame: {err}!"); } - MessageInbound::KeyboardEvent(event) => { - self.process_keyboard_event(&event); + }, + encoder_options, + ); + match res { + Ok(r) => video_encoder = Some(r), + Err(e) => { + warn!("{}", e); + continue; + } + }; + } + let video_encoder = video_encoder.as_mut().unwrap(); + video_encoder.encode(pixel_data); + } + // stop thread once the channel is closed + Err(RecvTimeoutError::Disconnected) => return, + }; + } +} + +pub struct WsWeylusReceiver { + recv: tokio::sync::mpsc::Receiver, +} + +impl Iterator for WsWeylusReceiver { + type Item = Result; + + fn next(&mut self) -> Option { + self.recv.blocking_recv().map(Ok) + } +} + +impl WeylusReceiver for WsWeylusReceiver { + type Error = Infallible; +} + +pub enum WsMessage { + Frame(Frame<'static>), + Video(Vec), + MessageOutbound(MessageOutbound), +} + +unsafe impl Send for WsMessage {} + +#[derive(Clone)] +pub struct WsWeylusSender { + sender: tokio::sync::mpsc::Sender, +} + +impl WeylusSender for WsWeylusSender { + type Error = tokio::sync::mpsc::error::SendError; + + fn send_message(&mut self, message: MessageOutbound) -> Result<(), Self::Error> { + self.sender + .blocking_send(WsMessage::MessageOutbound(message)) + } + + fn send_video(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { + self.sender.blocking_send(WsMessage::Video(bytes.to_vec())) + } +} + +pub fn weylus_websocket_channel( + websocket: WebSocket>, + semaphore_shutdown: Arc, +) -> (WsWeylusSender, WsWeylusReceiver) { + let (rx, mut tx) = websocket.split(|ws| tokio::io::split(ws)); + + let mut rx = FragmentCollectorRead::new(rx); + + let (sender_inbound, receiver_inbound) = channel::(32); + let (sender_outbound, mut receiver_outbound) = channel::(32); + + { + let sender_outbound = sender_outbound.clone(); + tokio::spawn(async move { + let mut send_fn = |frame| async { + if let Err(err) = sender_outbound.send(WsMessage::Frame(frame)).await { + warn!("Failed to send websocket frame while receiving fragmented frame: {err}.") + }; + Ok(()) + }; + + loop { + let fut = rx.read_frame::<_, WebSocketError>(&mut send_fn); + + let frame = tokio::select! { + _ = semaphore_shutdown.acquire() => break, + frame = fut => frame.unwrap(), + }; + match frame.opcode { + OpCode::Close => break, + OpCode::Text => match serde_json::from_slice(&frame.payload) { + Ok(msg) => { + if let Err(err) = sender_inbound.send(msg).await { + warn!("Failed to forward inbound message to WeylusClientHandler: {err}."); } - MessageInbound::TryGetFrame => self.queue_try_send_video_frame(), - MessageInbound::GetCapturableList => self.send_capturable_list(), - MessageInbound::Config(config) => self.setup(config), } + Err(err) => warn!("Failed to parse message: {err}"), + }, + _ => {} + } + } + }); + } + + tokio::spawn(async move { + loop { + let msg = if let Some(msg) = receiver_outbound.recv().await { + msg + } else { + break; + }; + + match msg { + WsMessage::Frame(frame) => { + if let Err(err) = tx.write_frame(frame).await { + if let WebSocketError::ConnectionClosed = err { + break; + } + warn!("Failed to send frame: {err}"); } - Err(err) => { - warn!("Unable to parse message: {} ({})", s, err); - self.send_msg(&MessageOutbound::Error( - "Failed to parse message!".to_string(), - )); + } + WsMessage::Video(data) => { + if let Err(err) = tx.write_frame(Frame::binary(data.into())).await { + if let WebSocketError::ConnectionClosed = err { + break; + } + warn!("Failed to send video frame: {err}"); + } + } + WsMessage::MessageOutbound(msg) => { + let json_string = serde_json::to_string(&msg).unwrap(); + let data = json_string.as_bytes(); + if let Err(err) = tx.write_frame(Frame::text(data.into())).await { + if let WebSocketError::ConnectionClosed = err { + break; + } + warn!("Failed to send outbound message: {err}"); } } } - _ => (), } - } + }); + + ( + WsWeylusSender { + sender: sender_outbound, + }, + WsWeylusReceiver { + recv: receiver_inbound, + }, + ) } diff --git a/src/weylus.rs b/src/weylus.rs index f4233f60..9c4dc6aa 100644 --- a/src/weylus.rs +++ b/src/weylus.rs @@ -1,29 +1,21 @@ use std::net::SocketAddr; -use std::sync::mpsc; -use tokio::sync::mpsc as mpsc_tokio; -use tracing::{error, warn}; +use std::sync::Arc; +use tracing::error; use crate::config::Config; use crate::video::EncoderOptions; -use crate::web::{Ui2WebMessage, Web2UiMessage}; -use crate::websocket::{Ui2WsMessage, Ws2UiMessage, WsConfig}; - -struct Channels { - sender_ui2ws: mpsc::Sender, - sender_ui2web: mpsc_tokio::Sender, -} +use crate::web::{Web2UiMessage, WebServerConfig, WebStartUpMessage}; +use crate::websocket::WeylusClientConfig; pub struct Weylus { - channels: Option, - ws_thread: Option>, + notify_shutdown: Arc, web_thread: Option>, } impl Weylus { pub fn new() -> Self { Self { - channels: None, - ws_thread: None, + notify_shutdown: Arc::new(tokio::sync::Notify::new()), web_thread: None, } } @@ -32,11 +24,7 @@ impl Weylus { &mut self, config: &Config, mut on_web_message: impl FnMut(Web2UiMessage) + Send + 'static, - mut on_ws_message: impl FnMut(Ws2UiMessage) + Send + 'static, ) -> bool { - if self.channels.is_some() { - return false; - } let encoder_options = EncoderOptions { #[cfg(target_os = "linux")] try_vaapi: config.try_vaapi, @@ -59,68 +47,34 @@ impl Weylus { try_mediafoundation: false, }; - let ws_config = WsConfig { - address: SocketAddr::new(config.bind_address, config.websocket_port), - access_code: config.access_code.clone(), - encoder_options, - #[cfg(target_os = "linux")] - wayland_support: config.wayland_support, - }; - - let (sender_ui2ws, receiver_ui2ws) = mpsc::channel(); - let (sender_ui2web, receiver_ui2web) = mpsc_tokio::channel(100); - - let (sender_ws2ui, receiver_ws2ui) = mpsc::channel(); - let (sender_web2ui, receiver_web2ui) = mpsc::channel(); - - let ws_thread = crate::websocket::run(sender_ws2ui, receiver_ui2ws, ws_config); - match receiver_ws2ui.recv() { - Ok(Ws2UiMessage::Start) => {} - Ok(Ws2UiMessage::Error(err)) => { - error!("Failed to start websocket server: {}", err); - if ws_thread.join().is_err() { - error!("Websocketserver thread panicked."); - } - return false; - } - Ok(Ws2UiMessage::UInputInaccessible) => unreachable!(), - Err(err) => { - error!("Error communicating with websocketserver thread: {}", err); - if ws_thread.join().is_err() { - error!("Websocketserver thread panicked."); - } - return false; - } - } + let (sender_ui, mut receiver_ui) = tokio::sync::mpsc::channel(100); + let (sender_startup, receiver_startup) = tokio::sync::oneshot::channel(); let web_thread = crate::web::run( - sender_web2ui, - receiver_ui2web, - SocketAddr::new(config.bind_address, config.web_port), - config.websocket_port, - config.access_code.clone(), - config.custom_index_html.clone(), - config.custom_access_html.clone(), - config.custom_style_css.clone(), - config.custom_lib_js.clone(), + sender_ui, + sender_startup, + self.notify_shutdown.clone(), + WebServerConfig { + bind_addr: SocketAddr::new(config.bind_address, config.web_port), + access_code: config.access_code.clone(), + custom_index_html: config.custom_index_html.clone(), + custom_access_html: config.custom_access_html.clone(), + custom_style_css: config.custom_style_css.clone(), + custom_lib_js: config.custom_lib_js.clone(), + }, + WeylusClientConfig { + encoder_options, + #[cfg(target_os = "linux")] + wayland_support: config.wayland_support, + }, ); - match receiver_web2ui.recv() { - Ok(Web2UiMessage::Start) => (), - Ok(Web2UiMessage::Error(err)) => { - error!("Webserver error: {}", err); + + match receiver_startup.blocking_recv() { + Ok(WebStartUpMessage::Start) => (), + Ok(WebStartUpMessage::Error) => { if web_thread.join().is_err() { error!("Webserver thread panicked."); } - - if let Err(err) = sender_ui2ws.send(Ui2WsMessage::Shutdown) { - warn!( - "Failed to send shutdown command to websocketserver: {}", - err - ); - } - if ws_thread.join().is_err() { - error!("Websocketserver thread panicked."); - } return false; } Err(err) => { @@ -128,60 +82,24 @@ impl Weylus { if web_thread.join().is_err() { error!("Webserver thread panicked."); } - - if let Err(err) = sender_ui2ws.send(Ui2WsMessage::Shutdown) { - warn!( - "Failed to send shutdown command to websocketserver: {}", - err - ); - } - if ws_thread.join().is_err() { - error!("Websocketserver thread panicked."); - } return false; } } - self.ws_thread = Some(ws_thread); self.web_thread = Some(web_thread); - self.channels = Some(Channels { - sender_ui2ws, - sender_ui2web, - }); std::thread::spawn(move || { - for msg in receiver_web2ui.iter() { + while let Some(msg) = receiver_ui.blocking_recv() { on_web_message(msg); } }); - std::thread::spawn(move || { - for msg in receiver_ws2ui.iter() { - on_ws_message(msg); - } - }); true } pub fn stop(&mut self) { - if let Some(channels) = self.channels.as_mut() { - if let Err(err) = channels.sender_ui2ws.send(Ui2WsMessage::Shutdown) { - warn!( - "Failed to send shutdown command to websocketserver: {}", - err - ); - } - if let Err(err) = channels.sender_ui2web.try_send(Ui2WebMessage::Shutdown) { - warn!("Failed to send shutdown command to webserver: {}", err); - } - } + self.notify_shutdown.notify_one(); self.wait(); - self.channels = None; } - pub fn wait(&mut self) { - if let Some(t) = self.ws_thread.take() { - if t.join().is_err() { - error!("Websocket thread panicked."); - } - } + fn wait(&mut self) { if let Some(t) = self.web_thread.take() { if t.join().is_err() { error!("Web thread panicked."); diff --git a/ts/lib.ts b/ts/lib.ts index 75fb8e50..28899754 100644 --- a/ts/lib.ts +++ b/ts/lib.ts @@ -16,7 +16,7 @@ let last_fps_calc: number = performance.now(); let check_video: HTMLInputElement; -function run(access_code: string, websocket_port: number, level: string) { +function run(level: string) { window.onload = () => { log_pre = document.getElementById("log") as HTMLPreElement; log_pre.textContent = ""; @@ -39,7 +39,7 @@ function run(access_code: string, websocket_port: number, level: string) { } return false; }, true) - init(access_code, websocket_port) + init(); }; } @@ -53,12 +53,12 @@ function log(level: LogLevel, msg: string) { log_pre.textContent += LogLevel[level] + ": " + msg + "\n"; } -function frame_update_scale(x: number) { - return Math.pow(x / 100, 3); +function frame_rate_scale(x: number) { + return Math.pow(x / 100, 1.5); } -function frame_update_scale_inv(x: number) { - return 100 * Math.pow(x, 1 / 3); +function frame_rate_scale_inv(x: number) { + return 100 * Math.pow(x, 2 / 3); } @@ -101,8 +101,8 @@ class Settings { webSocket: WebSocket; checks: Map; capturable_select: HTMLSelectElement; - frame_update_limit_input: HTMLInputElement; - frame_update_limit_output: HTMLOutputElement; + frame_rate_input: HTMLInputElement; + frame_rate_output: HTMLOutputElement; scale_video_input: HTMLInputElement; scale_video_output: HTMLOutputElement; range_min_pressure: HTMLInputElement; @@ -115,16 +115,16 @@ class Settings { this.webSocket = webSocket; this.checks = new Map(); this.capturable_select = document.getElementById("window") as HTMLSelectElement; - this.frame_update_limit_input = document.getElementById("frame_update_limit") as HTMLInputElement; - this.frame_update_limit_input.min = frame_update_scale_inv(1).toString(); - this.frame_update_limit_input.max = frame_update_scale_inv(1000).toString(); - this.frame_update_limit_output = this.frame_update_limit_input.nextElementSibling as HTMLOutputElement; + this.frame_rate_input = document.getElementById("frame_rate") as HTMLInputElement; + this.frame_rate_input.min = frame_rate_scale_inv(0).toString(); + this.frame_rate_input.max = frame_rate_scale_inv(120).toString(); + this.frame_rate_output = this.frame_rate_input.nextElementSibling as HTMLOutputElement; this.scale_video_input = document.getElementById("scale_video") as HTMLInputElement; this.scale_video_output = this.scale_video_input.nextElementSibling as HTMLOutputElement; this.range_min_pressure = document.getElementById("min_pressure") as HTMLInputElement; this.client_name_input = document.getElementById("client_name") as HTMLInputElement; - this.frame_update_limit_input.oninput = (e) => { - this.frame_update_limit_output.value = Math.round(frame_update_scale(this.frame_update_limit_input.valueAsNumber)).toString(); + this.frame_rate_input.oninput = (e) => { + this.frame_rate_output.value = Math.round(frame_rate_scale(this.frame_rate_input.valueAsNumber)).toString(); } this.scale_video_input.oninput = (e) => { let [w, h] = calc_max_video_resolution(this.scale_video_input.valueAsNumber) @@ -189,7 +189,7 @@ class Settings { toggle_energysaving((e.target as HTMLInputElement).checked); }; - this.frame_update_limit_input.onchange = () => this.save_settings(); + this.frame_rate_input.onchange = () => this.save_settings(); this.range_min_pressure.onchange = () => this.save_settings(); // server @@ -198,6 +198,7 @@ class Settings { this.checks.get("capture_cursor").onchange = upd_server_config; this.scale_video_input.onchange = upd_server_config; this.client_name_input.onchange = upd_server_config; + this.frame_rate_input.onchange = upd_server_config; document.getElementById("refresh").onclick = () => this.webSocket.send('"GetCapturableList"'); this.capturable_select.onchange = () => this.send_server_config(); @@ -213,6 +214,7 @@ class Settings { let [w, h] = calc_max_video_resolution(this.scale_video_input.valueAsNumber); config["max_width"] = w; config["max_height"] = h; + config["frame_rate"] = frame_rate_scale(this.frame_rate_input.valueAsNumber); if (this.client_name_input.value) config["client_name"] = this.client_name_input.value; this.webSocket.send(JSON.stringify({ "Config": config })); @@ -222,7 +224,7 @@ class Settings { let settings = Object(null); for (const [key, elem] of this.checks.entries()) settings[key] = elem.checked; - settings["frame_update_limit"] = frame_update_scale(this.frame_update_limit_input.valueAsNumber).toString(); + settings["frame_rate"] = frame_rate_scale(this.frame_rate_input.valueAsNumber).toString(); settings["scale_video"] = this.scale_video_input.value; settings["min_pressure"] = this.range_min_pressure.value; settings["client_name"] = this.client_name_input.value; @@ -232,8 +234,8 @@ class Settings { load_settings() { let settings_string = localStorage.getItem("settings"); if (settings_string === null) { - this.frame_update_limit_input.value = frame_update_scale_inv(33).toString(); - this.frame_update_limit_output.value = (33).toString(); + this.frame_rate_input.value = frame_rate_scale_inv(30).toString(); + this.frame_rate_output.value = (30).toString(); let [w, h] = calc_max_video_resolution(this.scale_video_input.valueAsNumber) this.scale_video_output.value = w + "x" + h; return; @@ -244,12 +246,12 @@ class Settings { if (typeof settings[key] === "boolean") elem.checked = settings[key]; } - let upd_limit = settings["frame_update_limit"]; + let upd_limit = settings["frame_rate"]; if (upd_limit) - this.frame_update_limit_input.value = frame_update_scale_inv(upd_limit).toString(); + this.frame_rate_input.value = frame_rate_scale_inv(upd_limit).toString(); else - this.frame_update_limit_input.value = frame_update_scale_inv(33).toString(); - this.frame_update_limit_output.value = Math.round(frame_update_scale(this.frame_update_limit_input.valueAsNumber)).toString(); + this.frame_rate_input.value = frame_rate_scale_inv(30).toString(); + this.frame_rate_output.value = Math.round(frame_rate_scale(this.frame_rate_input.valueAsNumber)).toString(); let scale_video = settings["scale_video"]; if (scale_video) @@ -302,10 +304,6 @@ class Settings { return ptrs; } - frame_update_limit() { - return frame_update_scale(this.frame_update_limit_input.valueAsNumber) - } - toggle() { this.settings.classList.toggle("hide"); this.visible = !this.visible; @@ -315,7 +313,7 @@ class Settings { let current_selection = undefined; if (this.capturable_select.selectedOptions[0]) current_selection = this.capturable_select.selectedOptions[0].textContent; - let new_index; + let new_index: number; this.capturable_select.innerText = ""; window_names.forEach((name, i) => { let option = document.createElement("option"); @@ -354,8 +352,7 @@ class PEvent { width: number; height: number; - constructor(eventType: string, event: PointerEvent, target: HTMLElement) { - let targetRect = target.getBoundingClientRect(); + constructor(eventType: string, event: PointerEvent, targetRect: DOMRect) { let diag_len = Math.sqrt(targetRect.width * targetRect.width + targetRect.height * targetRect.height) this.event_type = eventType.toString(); this.pointer_id = event.pointerId; @@ -581,8 +578,12 @@ class Painter { } onmove(event: PointerEvent) { - if (this.lines_active.has(event.pointerId)) - this.appendEventToLine(event); + if (this.lines_active.has(event.pointerId)) { + const events = typeof event.getCoalescedEvents === 'function' ? event.getCoalescedEvents() : [event]; + for (const e of events) { + this.appendEventToLine(e); + } + } } onstop(event: PointerEvent) { @@ -641,17 +642,21 @@ class PointerHandler { onEvent(event: PointerEvent, event_type: string) { if (this.pointerTypes.includes(event.pointerType)) { - this.webSocket.send( - JSON.stringify( - { - "PointerEvent": new PEvent( - event_type, - event, - event.target as HTMLElement - ) - } - ) - ); + let rect = (event.target as HTMLElement).getBoundingClientRect(); + const events = event_type === "pointermove" && typeof event.getCoalescedEvents === 'function' ? event.getCoalescedEvents() : [event]; + for (let event of events) { + this.webSocket.send( + JSON.stringify( + { + "PointerEvent": new PEvent( + event_type, + event, + rect + ) + } + ) + ); + } if (settings.visible) { settings.toggle(); } @@ -687,19 +692,37 @@ class KeyboardHandler { constructor(webSocket: WebSocket) { this.webSocket = webSocket; - let m = document.getElementById("main"); + let d = document; + let s = document.getElementById("settings") + + // Consume all KeyboardEvents, except the settings menu is open. + // this avoids making the main/video/canvas element focusable by using + // a tabindex which interferes with PointerEvent than can be considered + // hovering. - m.onkeydown = (e) => { + function settings_hidden() { + return s.classList.contains("hide") || s.classList.contains("vanish"); + } + + d.onkeydown = (e) => { + if (!settings_hidden()) + return true; if (e.repeat) return this.onEvent(e, "repeat"); return this.onEvent(e, "down"); }; - m.onkeyup = (e) => { return this.onEvent(e, "up") }; - m.onkeypress = (e) => { + d.onkeyup = (e) => { + if (!settings_hidden()) + return true; + return this.onEvent(e, "up"); + }; + d.onkeypress = (e) => { + if (!settings_hidden()) + return true; e.preventDefault(); e.stopPropagation(); return false; - } + }; } onEvent(event: KeyboardEvent, event_type: string) { @@ -710,27 +733,13 @@ class KeyboardHandler { } } -function frame_timer(webSocket: WebSocket) { - // Closing or closed, so no more frames - if (webSocket.readyState > webSocket.OPEN) - return; - +function frame_rate_stats() { let t = performance.now(); - if (t - last_fps_calc > 1500) { - let fps = Math.round(frame_count / (t - last_fps_calc) * 10000) / 10; - fps_out.value = fps.toString(); - frame_count = 0; - last_fps_calc = t; - } - - if (document.hidden) { - requestAnimationFrame(() => frame_timer(webSocket)); - return; - } - - if (webSocket.readyState === webSocket.OPEN && check_video.checked) - webSocket.send('"TryGetFrame"'); - setTimeout(() => frame_timer(webSocket), settings.frame_update_limit()); + let fps = Math.round(frame_count / (t - last_fps_calc) * 10000) / 10; + fps_out.value = fps.toString(); + frame_count = 0; + last_fps_calc = t; + setTimeout(() => frame_rate_stats(), 1500); } function handle_messages( @@ -836,12 +845,11 @@ function check_apis() { } } -function init(access_code: string, websocket_port: number) { +function init() { check_apis(); - let authed = false; let protocol = document.location.protocol == "https:" ? "wss://" : "ws://"; - let webSocket = new WebSocket(protocol + window.location.hostname + ":" + websocket_port); + let webSocket = new WebSocket(protocol + window.location.hostname + ":" + window.location.port + "/ws"); webSocket.binaryType = "arraybuffer"; settings = new Settings(webSocket); @@ -886,8 +894,7 @@ function init(access_code: string, websocket_port: number) { canvas.height = window.innerHeight * window.devicePixelRatio; let [w, h] = calc_max_video_resolution(settings.scale_video_input.valueAsNumber); settings.scale_video_output.value = w + "x" + h; - if (authed) - settings.send_server_config(); + settings.send_server_config(); } video.controls = false; video.onloadeddata = () => stretch_video(); @@ -896,7 +903,6 @@ function init(access_code: string, websocket_port: number) { if (!is_connected) { new KeyboardHandler(webSocket); new PointerHandler(webSocket); - frame_timer(webSocket); is_connected = true; } }, @@ -905,12 +911,10 @@ function init(access_code: string, websocket_port: number) { ); window.onunload = () => { webSocket.close(); } webSocket.onopen = function(event) { - if (access_code) - webSocket.send(access_code); - authed = true; webSocket.send('"GetCapturableList"'); settings.send_server_config(); } + frame_rate_stats(); } // object-fit: fill; <-- this is unfortunately not supported on iOS, so we use the following diff --git a/www/templates/index.html b/www/templates/index.html index 411ef40c..83d8396d 100644 --- a/www/templates/index.html +++ b/www/templates/index.html @@ -1,72 +1,80 @@ - - - - - - Weylus - - - - - -
- - -
-
-
-
-

Settings

-
- - - -
-

Video

-
- - - - - - - - -
-

Input

-
- - - - - -
-
- -
-
- - -
-
- -
-
-
-
+ + + + + + Weylus + + + + + + +
+ + +
+
+
+
+

Settings

+
+ + + +
+

Video

+
+ + + + + + + + +
+

Input

+
+ + + + + +
+
+ +
+
+ + +
+
+ +
+
+
- +
+