From b26012248a347ddb5cde9f4d00f916df6361adcb Mon Sep 17 00:00:00 2001 From: BenTech2 <5576214+BenTech2@users.noreply.github.com> Date: Wed, 21 Oct 2026 13:28:02 -0400 Subject: [PATCH 1/3] Silkscreen 0.2.0: several displays, mirroring, PyroWave, tray, live settings Hosting - Extend to several displays at once (each its own virtual monitor) or mirror a screen to many; mirrors share one capture and one encode, each display still gets its own encrypted stream. --connect and --mirror are repeatable. - Stream settings apply live while connected: bitrate, codec, encoder and packet size switch in place; source, resolution, size and refresh reconnect briefly. - Frame rate limit (--max-fps) for slow links; a full send queue drops a packet instead of ending the session. - macOS: unique virtual display serials, existing-screen capture keeps its aspect ratio; virtual monitor creation is serialized on every OS. Display - Skip ahead when the picture falls behind (--skip-behind MS): measures queueing delay per frame, drains the backlog and resumes at a keyframe. - Hardware decoding toggles mid-stream (Ctrl/Cmd+Alt/Option+Shift+H); the debug overlay shows the live decoder and how far behind the picture is. - Waiting screen: app name, display name and a shortcut list. App - Every CLI option has a GUI equivalent; settings are saved in gui.json. - Optional tray / menu bar icon (closing the window keeps Silkscreen running) and start at login (window, tray or display mode); both off by default. macOS menu bar template icon, Dock icon and icon on Apple's grid. - Header bar redesigned; the log level can be raised from the Debug tab. - Fixes: the macOS window drew nothing (font atlas dropped on the first frame); ~11k redraws/s while the window was hidden; Windows prefers Direct3D 12 (Intel Vulkan drivers crashed on device creation). - Windows: log file in %APPDATA%\silkscreen\config, panic message box. Codec - PyroWave (experimental): GPU wavelet codec via Metal on macOS and Vulkan elsewhere, vendored as submodules (scripts/fetch-submodules.sh). The Vulkan library is loaded on first use and probed in a separate process, so a blocked DLL or a driver crash only disables PyroWave. - Protocol version 3 (new codec): all devices need this release. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 14 +- .github/workflows/release.yml | 36 +- .gitmodules | 6 + Cargo.lock | 349 +++++++- Cargo.toml | 2 +- android/app/build.gradle.kts | 4 +- assets/icon-macos.svg | 22 + assets/png/macos/icon-1024.png | Bin 0 -> 34616 bytes assets/png/macos/icon-128.png | Bin 0 -> 3306 bytes assets/png/macos/icon-16.png | Bin 0 -> 496 bytes assets/png/macos/icon-256.png | Bin 0 -> 6942 bytes assets/png/macos/icon-32.png | Bin 0 -> 861 bytes assets/png/macos/icon-512.png | Bin 0 -> 15185 bytes assets/png/macos/icon-64.png | Bin 0 -> 1628 bytes assets/tray-macos-36.rgba | Bin 0 -> 5184 bytes assets/tray-macos.svg | 16 + crates/silkscreen-android/src/codec.rs | 2 + crates/silkscreen-android/src/nal.rs | 2 +- crates/silkscreen-codec/Cargo.toml | 11 + crates/silkscreen-codec/build.rs | 114 +++ crates/silkscreen-codec/src/decoder.rs | 67 +- crates/silkscreen-codec/src/encoder.rs | 98 ++- crates/silkscreen-codec/src/lib.rs | 2 + crates/silkscreen-codec/src/pyrowave.rs | 729 ++++++++++++++++ .../src/pyrowave_metal_shim.mm | 91 ++ crates/silkscreen-core/src/service.rs | 119 ++- crates/silkscreen-core/src/status.rs | 4 + crates/silkscreen-platform/src/autostart.rs | 172 ++++ crates/silkscreen-platform/src/lib.rs | 36 + crates/silkscreen-platform/src/macos/mod.rs | 27 + .../src/macos/virtual_display.rs | 13 +- crates/silkscreen-platform/src/windows/mod.rs | 38 +- crates/silkscreen-proto/src/control.rs | 5 + crates/silkscreen-proto/src/lib.rs | 2 +- crates/silkscreen/Cargo.toml | 12 + crates/silkscreen/src/app/mod.rs | 586 +++++++++++-- crates/silkscreen/src/app/prefs.rs | 170 ++++ crates/silkscreen/src/app/render.rs | 123 ++- crates/silkscreen/src/app/tray.rs | 131 +++ crates/silkscreen/src/app/ui.rs | 821 ++++++++++++++---- crates/silkscreen/src/display/mod.rs | 50 +- crates/silkscreen/src/host.rs | 368 ++++++-- crates/silkscreen/src/main.rs | 257 ++++-- crates/silkscreen/src/mirror.rs | 457 ++++++++++ crates/silkscreen/src/pacer.rs | 86 ++ scripts/fetch-submodules.sh | 8 + scripts/package-linux.sh | 9 +- scripts/package-windows.sh | 10 +- scripts/render-icons.sh | 12 +- third_party/granite | 1 + third_party/pyrowave | 1 + 51 files changed, 4628 insertions(+), 455 deletions(-) create mode 100644 .gitmodules create mode 100644 assets/icon-macos.svg create mode 100644 assets/png/macos/icon-1024.png create mode 100644 assets/png/macos/icon-128.png create mode 100644 assets/png/macos/icon-16.png create mode 100644 assets/png/macos/icon-256.png create mode 100644 assets/png/macos/icon-32.png create mode 100644 assets/png/macos/icon-512.png create mode 100644 assets/png/macos/icon-64.png create mode 100644 assets/tray-macos-36.rgba create mode 100644 assets/tray-macos.svg create mode 100644 crates/silkscreen-codec/src/pyrowave.rs create mode 100644 crates/silkscreen-codec/src/pyrowave_metal_shim.mm create mode 100644 crates/silkscreen-platform/src/autostart.rs create mode 100644 crates/silkscreen/src/app/prefs.rs create mode 100644 crates/silkscreen/src/app/tray.rs create mode 100644 crates/silkscreen/src/mirror.rs create mode 100644 crates/silkscreen/src/pacer.rs create mode 100755 scripts/fetch-submodules.sh create mode 160000 third_party/granite create mode 160000 third_party/pyrowave diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 280c84a..49549b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - run: scripts/fetch-submodules.sh - uses: dtolnay/rust-toolchain@stable with: components: clippy, rustfmt @@ -20,7 +21,7 @@ jobs: sudo apt-get update sudo apt-get install -y clang pkg-config libavcodec-dev libavformat-dev libavutil-dev \ libavfilter-dev libavdevice-dev libswscale-dev libswresample-dev libpipewire-0.3-dev \ - libdbus-1-dev libxkbcommon-dev libwayland-dev + libdbus-1-dev libxkbcommon-dev libwayland-dev cmake ninja-build - run: cargo fmt --all --check - run: cargo clippy --all-targets -- -D warnings - run: cargo test @@ -28,12 +29,16 @@ jobs: - uses: actions/upload-artifact@v4 with: name: silkscreen-linux-x64 - path: target/release/silkscreen + path: | + target/release/silkscreen + target/release/libpyrowave-shared.so* windows: runs-on: windows-latest steps: - uses: actions/checkout@v4 + - run: scripts/fetch-submodules.sh + shell: bash - uses: dtolnay/rust-toolchain@stable - name: Fetch FFmpeg (LGPL, shared) shell: pwsh @@ -50,8 +55,10 @@ jobs: run: | New-Item -ItemType Directory dist/silkscreen-windows-x64 | Out-Null Copy-Item target/release/silkscreen.exe dist/silkscreen-windows-x64/ + Copy-Item target/release/*pyrowave*.dll dist/silkscreen-windows-x64/ + Copy-Item third_party/pyrowave/LICENSE dist/silkscreen-windows-x64/PYROWAVE-LICENSE.txt foreach ($lib in "avcodec", "avutil", "swresample") { Copy-Item "$env:FFMPEG_DIR/bin/$lib-*.dll" dist/silkscreen-windows-x64/ } - Copy-Item README.md dist/silkscreen-windows-x64/ + Copy-Item README.md, LICENSE-MIT, LICENSE-APACHE, THIRD-PARTY-NOTICES.md, THIRD-PARTY-LICENSES.md dist/silkscreen-windows-x64/ - uses: actions/upload-artifact@v4 with: name: silkscreen-windows-x64 @@ -61,6 +68,7 @@ jobs: runs-on: macos-15 steps: - uses: actions/checkout@v4 + - run: scripts/fetch-submodules.sh - uses: dtolnay/rust-toolchain@stable - run: brew install ffmpeg pkg-config - run: cargo test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a02cbe7..dbb224c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,14 +1,15 @@ name: Release -# Builds what can only be built on GitHub's runners (macOS) and attaches it -# to a published release. The other assets are built with scripts/. +# Builds the macOS app and the Linux / Raspberry Pi bundles and attaches them +# to a published release. The Windows zip and the Android APK are built with +# scripts/ (package-windows.sh, build-android.sh) and uploaded by hand. on: release: types: [published] workflow_dispatch: inputs: tag: - description: Release tag to attach the macOS build to + description: Release tag to attach the builds to required: true permissions: @@ -21,6 +22,7 @@ jobs: TAG: ${{ github.event.release.tag_name || inputs.tag }} steps: - uses: actions/checkout@v4 + - run: scripts/fetch-submodules.sh - uses: dtolnay/rust-toolchain@stable - run: brew install ffmpeg pkg-config dylibbundler - run: cargo build --release -p silkscreen @@ -32,11 +34,11 @@ jobs: cp target/release/silkscreen "$app/MacOS/" # Copy Homebrew's FFmpeg and its dependencies into the bundle. dylibbundler -od -b -x "$app/MacOS/silkscreen" -d "$app/Frameworks" -p @executable_path/../Frameworks/ - # App icon from the PNGs rendered by scripts/render-icons.sh. + # App icon (on Apple's icon grid) rendered by scripts/render-icons.sh. set=$RUNNER_TEMP/silkscreen.iconset && mkdir -p "$set" for s in 16 32 128 256 512; do - cp assets/png/icon-$s.png "$set/icon_${s}x${s}.png" - cp assets/png/icon-$((s * 2)).png "$set/icon_${s}x${s}@2x.png" + cp assets/png/macos/icon-$s.png "$set/icon_${s}x${s}.png" + cp assets/png/macos/icon-$((s * 2)).png "$set/icon_${s}x${s}@2x.png" done iconutil -c icns "$set" -o "$app/Resources/silkscreen.icns" version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "silkscreen") | .version') @@ -44,9 +46,29 @@ jobs: # Rewriting load paths invalidates signatures; Apple Silicon needs one. for f in "$app"/Frameworks/*.dylib; do codesign --force -s - "$f"; done codesign --force -s - "$out/Silkscreen.app" - cp README.md "$out/" + cp README.md LICENSE-MIT LICENSE-APACHE THIRD-PARTY-NOTICES.md THIRD-PARTY-LICENSES.md "$out/" + cp third_party/pyrowave/LICENSE "$out/PYROWAVE-LICENSE.txt" "$app/MacOS/silkscreen" doctor (cd dist && zip -qry silkscreen-macos-arm64.zip silkscreen-macos-arm64) - run: gh release upload "$TAG" dist/silkscreen-macos-arm64.zip --clobber env: GH_TOKEN: ${{ github.token }} + + linux: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + arch: [x86_64, aarch64] + env: + TAG: ${{ github.event.release.tag_name || inputs.tag }} + steps: + - uses: actions/checkout@v4 + - run: scripts/fetch-submodules.sh + # The Raspberry Pi (aarch64) bundle builds under emulation. + - uses: docker/setup-qemu-action@v3 + if: matrix.arch == 'aarch64' + - run: scripts/package-linux.sh ${{ matrix.arch }} + - run: gh release upload "$TAG" dist/silkscreen-linux-${{ matrix.arch }}.tar.gz --clobber + env: + GH_TOKEN: ${{ github.token }} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c6eb30b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "third_party/pyrowave"] + path = third_party/pyrowave + url = https://github.com/Themaister/pyrowave.git +[submodule "third_party/granite"] + path = third_party/granite + url = https://github.com/Themaister/Granite.git diff --git a/Cargo.lock b/Cargo.lock index b5b51be..3e08df4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,6 +217,15 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arc-swap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" +dependencies = [ + "rustversion", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -241,7 +250,7 @@ version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] @@ -271,6 +280,79 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.5", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.5", +] + [[package]] name = "async-recursion" version = "1.2.0" @@ -282,6 +364,30 @@ dependencies = [ "syn 3.0.9", ] +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.5", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.92" @@ -413,6 +519,19 @@ dependencies = [ "objc2 0.6.5", ] +[[package]] +name = "blocking" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -610,7 +729,7 @@ checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.9", ] [[package]] @@ -662,6 +781,15 @@ dependencies = [ "error-code", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "cobs" version = "0.3.0" @@ -823,6 +951,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.23" @@ -954,7 +1091,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] @@ -1338,6 +1475,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.34" @@ -1925,6 +2071,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyboard-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" +dependencies = [ + "bitflags 2.13.2", + "serde", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -1932,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading", + "libloading 0.8.9", "pkg-config", ] @@ -1942,6 +2098,26 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "ksni" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bcc1cb43797140f74b711f1c4a49f48a7a7429f211c5fb78264e7bcd1e99caf" +dependencies = [ + "arc-swap", + "async-executor", + "async-io", + "async-lock", + "crossbeam-queue", + "futures-channel", + "futures-lite", + "futures-util", + "pastey", + "serde", + "task-local", + "zbus", +] + [[package]] name = "kurbo" version = "0.13.1" @@ -1976,6 +2152,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -2162,6 +2348,24 @@ dependencies = [ "pxfm", ] +[[package]] +name = "muda" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4340e89c178916e62879c5c36b7f699019369be413e496178a6983ce91b8bc03" +dependencies = [ + "crossbeam-channel", + "dpi", + "keyboard-types", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "png", + "thiserror 2.0.21", + "windows-sys 0.61.2", +] + [[package]] name = "naga" version = "30.0.1" @@ -2350,8 +2554,8 @@ dependencies = [ "block2 0.5.1", "libc", "objc2 0.5.3", - "objc2-core-data", - "objc2-core-image", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", ] @@ -2364,9 +2568,17 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.13.2", "block2 0.6.2", + "libc", "objc2 0.6.5", + "objc2-cloud-kit 0.3.2", + "objc2-core-data 0.3.2", + "objc2-core-foundation", "objc2-core-graphics", + "objc2-core-image 0.3.2", + "objc2-core-text", + "objc2-core-video", "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", ] [[package]] @@ -2392,6 +2604,17 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-contacts" version = "0.2.2" @@ -2437,6 +2660,17 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -2478,6 +2712,16 @@ dependencies = [ "objc2-metal 0.2.2", ] +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-location" version = "0.2.2" @@ -2506,6 +2750,18 @@ dependencies = [ "objc2-core-video", ] +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-core-graphics", +] + [[package]] name = "objc2-core-video" version = "0.3.2" @@ -2665,9 +2921,9 @@ dependencies = [ "bitflags 2.13.2", "block2 0.5.1", "objc2 0.5.3", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", + "objc2-cloud-kit 0.2.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", @@ -2838,6 +3094,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "peniko" version = "0.6.1" @@ -2894,6 +3156,17 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pipewire" version = "0.10.1" @@ -3478,7 +3751,7 @@ dependencies = [ [[package]] name = "silkscreen" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "bytemuck", @@ -3492,6 +3765,9 @@ dependencies = [ "gethostname", "hex", "mdns-sd", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", "pollster", "postcard", "rand", @@ -3507,13 +3783,14 @@ dependencies = [ "spake2", "tracing", "tracing-subscriber", + "tray-icon", "wgpu", "winit", ] [[package]] name = "silkscreen-android" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "jni 0.21.1", @@ -3529,10 +3806,13 @@ dependencies = [ [[package]] name = "silkscreen-codec" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", + "cc", + "cmake", "ffmpeg-sys-next", + "libloading 0.9.0", "rayon", "silkscreen-proto", "tracing", @@ -3540,7 +3820,7 @@ dependencies = [ [[package]] name = "silkscreen-core" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "crossbeam-channel", @@ -3563,7 +3843,7 @@ dependencies = [ [[package]] name = "silkscreen-platform" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "ashpd", @@ -3595,7 +3875,7 @@ dependencies = [ [[package]] name = "silkscreen-proto" -version = "0.1.0" +version = "0.2.0" dependencies = [ "chacha20poly1305", "postcard", @@ -3878,6 +4158,15 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +[[package]] +name = "task-local" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2972044a9e5e448a506a7ff6f0d03b566d8ef4cd6918a58fc59835a0f8666626" +dependencies = [ + "pin-project-lite", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -4139,6 +4428,26 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tray-icon" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5cc5858ecf9574ddd8f0032a2ff7ffd6f7724d2b81e48b9374984b3fd6ddc9" +dependencies = [ + "crossbeam-channel", + "ksni", + "muda", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "once_cell", + "png", + "thiserror 2.0.21", + "windows-sys 0.61.2", +] + [[package]] name = "tree_magic_mini" version = "3.2.2" @@ -4715,7 +5024,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading", + "libloading 0.8.9", "log", "naga", "naga-types", @@ -5172,7 +5481,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading", + "libloading 0.8.9", "once_cell", "rustix 1.1.5", "x11rb-protocol", @@ -5245,8 +5554,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", "async-recursion", + "async-task", "async-trait", + "blocking", "enumflags2", "event-listener", "futures-core", diff --git a/Cargo.toml b/Cargo.toml index 4795238..4f3fefd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "3" members = ["crates/*"] [workspace.package] -version = "0.1.0" +version = "0.2.0" edition = "2024" license = "MIT OR Apache-2.0" repository = "https://github.com/BenTech2/silkscreen" diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 77ddf53..a0c23e4 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -13,8 +13,8 @@ android { // 30: MediaCodec low-latency mode, WindowMetrics, libnativewindow. minSdk = 30 targetSdk = 36 - versionCode = 1 - versionName = "0.1.0" + versionCode = 2 + versionName = "0.2.0" ndk { // Phones/tablets, plus x86_64 for the emulator. abiFilters += listOf("arm64-v8a", "x86_64") diff --git a/assets/icon-macos.svg b/assets/icon-macos.svg new file mode 100644 index 0000000..aa95dea --- /dev/null +++ b/assets/icon-macos.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/png/macos/icon-1024.png b/assets/png/macos/icon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..d53e475b709e624b1f5122fee08acdae2e460b42 GIT binary patch literal 34616 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�?X~&c|{J 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 pGeb(&ad4IPf1m%O1eC04^b?}u_1M$D;H~Vw literal 0 HcmV?d00001 diff --git a/assets/png/macos/icon-128.png b/assets/png/macos/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..f94a30ed4357e10242707ee9287996caea22f2d7 GIT binary patch literal 3306 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 literal 0 HcmV?d00001 diff --git a/assets/png/macos/icon-16.png b/assets/png/macos/icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..f4821946303fe7a9d7661845c0a244e0234faaba GIT binary patch literal 496 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 DgRRS4 literal 0 HcmV?d00001 diff --git a/assets/png/macos/icon-32.png b/assets/png/macos/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..7d13460acbfdaad071a393fefe3b4223d4f27a81 GIT binary patch literal 861 zcmV-j1ETziP)6k!<0KeIct?yF6qSROJ2E4M=t)M1e*tgu5-&<~KJ z!b%V#=+vL!A+Sr`I#g6dc<4|bk_dvN2r8m3X;!9Xxz)PvxbCm{(Lq;cc4v2WT?wKu z47@*{d7jTa@AJO%;qh<#xSS27j)`7@Cx~vAGd=>E33wl918l>V39sF#KCtJE z2hZ7=`SL#so;5W!>xQG@p*NYB9y`EfN;+Q7uXaR8ozwv%xXaNLA|#qpo&(D#_@(Cq#da*kTj}$f$5N zUhkjg&9gotp|1t)D#S8&c3h&j`zS^_SquP3xq><|Ezk@>|J^RIif@~S&AX_osv{a2 z$(i%ynwzuFJoaN&aIfsc>+`#qW$bdls&I)fKs!!$QN6keOp{b1#=|QeSZ49#JnuG) z#bOHrcy<3Wfo(_d`g{z%=yeHhki7+S0OGcG_m!&*e)@u}xFkuEU_+;4C^0?B)Wi?G zUYW#H*fD1T$t?e_0CvJ(;h$W)b=&0?kUSpRFLus=%&xDIN)Y)zgjilnJo?*dFG*PQ z$$*=In=I2HJT^%1@C9NMp`7`RyW8j5il{{KR}q`|>gwNdAyHRt83$I)2cmMtO4bJt zp!h{WJ3XCZ`1Kzf2nDM+p#T!+mbh`YEut_@ZH`oVF5ZbDNmN@;~&=_CuJwpDp9ce00000NkvXXu0mjfeoB)b literal 0 HcmV?d00001 diff --git a/assets/png/macos/icon-512.png b/assets/png/macos/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..c0cc7e6add2f9084ff35cfbb66890fdf74d6b6fe GIT binary patch literal 15185 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


Z+|dUC9>zc5LpO}*DcXQ-4W6x zNnQ|V^tBzSzeK_Eni`_<&G-$;7oNB}Sht-f5H-#3ezZW+2F7V|mi)aW>JrpP>EWY(kK-yEd30-C=|)xFt)CZIhjRbQkQq18 zUD>l$4ms|KY0ntgo^BelkDSArw-MT-@w7bI`qD|SM3&xQET5{+X{%3r`=mQh4i2)l zYS7df5fdoEKkdMg^I0yYR&KMcPLD+KdkmKXAGEgEi6j)uVWrzV>k{(~s*rjnnyqL;Ihj1@``J<+97}Jn(Ig zKF0Q~U(zK~7 zz%qI1It2^uJrLK1#63KAMNOnD)OSVS3|g-6TvYE_dlwh3>wM5Y|AV_QGT~uZeNF_u z+`Vd7J3}B5>NRwTMYG4Fp&v0Nm2U@o1zBcVGD=)HUvd29Bct)5N_b*gPMwv9=SQI{ zZn9UtT@O>0xu9906X@|?eCnFeHHrW%?W**{M{sp-ZO`RL%{dQmnwV%-Xpyb%9QFIR z85=35DPM|85E>3Ojp0gS8CgCPWc6+YUkX=;)Cgg7sFM4qjUaWz!3vdExS=1{bSYC? zeo}GR_pbyFbq3Ydth;TmhFXUD$T8fm2K}oK6{@ZFqk7POmaUdpxs%aVJi##@y}gvV zV*&G>9kTFJ2wCO$Wq-j`qHF zq&hc$n8+V|slMq{Jr`O`i7>?0b9zg75}^hSs}eZUjJiVcETZ=+GV5*rrj@~>QvzCZ z$JIx?`Un<D%nzpE8@{ELILC@D*1L>tYQ-)nXsDRb1= zbcOa}{sC^gqjAXyOXZ=_a4GZSTTr~DN55jT@o(Ue3n}O0#-Yb?b;)0WR5yx)8K>*Y zI8{?-kiHCdxSlRP)=?nY{6Wb`;T|t;bang2TtNF~PpeyD8qjYwp|6|#gZy^#fK+Nl zFC9)b7e6tkOW$Eb@f6?Z|B=TpWv7>+GqP+DExu2f1K0&wpi*!vO%08Tjn?ZCRqQ@K!W pJQuLPl}!aDTK+$JVEBk?s;^V%TAfOvoGt)5w+(NV-E?^RKLFpyTdn{A literal 0 HcmV?d00001 diff --git a/assets/png/macos/icon-64.png b/assets/png/macos/icon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..d593dba8f10044c6b838f4c3981354277c15e4d8 GIT binary patch literal 1628 zcmV-i2BZ0jP)hHQ(9|fP2K7PF zL?1N9;Qf;5i!a6(qs9k8qsFMwL@fe>cY+rrMpFb5m9j0RYEP>zyKT4KJ$uH7B?`E6 z&Ys;p$AbGy_M!XD%=yo^-^_e7X9^`slqgZ+|BJ}CUQ5f8OUBAW9bqYF$dpeL!WS&6 zG8?2cQzB^UlS1_93F8CyZu$z4PTO(nfS}fT)!m_K;(p+Grw?{U>4Bd&J`bdF zt(3b1=5(B)3E#WGtUSbdi1=AYu3+~EyK*X>Q=2V|muSJzJ3w_I(mdpVo=RO$^rMgM z3feoMZQ*QfqI?@rQw(Ju64L{|3+*DoXVXYfo0ztqqp$M-)mqAY53rn^{Z>Y;doXe?kMseO(d78Yyp*F0%n$Bx}(Tr64EzQWm83fAb{qQ9`6atMifiF zG6IAl!W<(JAP`}dO*sch()n(brW{s%u}g%Gd6Yu{(y08?+S1I8E0PQu@u6>3`q%T40$u&^ zO;j>G{?GUIiymRcqWs#N96vyB*Jte5ycQ#wa8%dxRiLXMHtssS^Mw*Fk8tV*%ei3X zqmJuZM1X4S6qX!qi)PL(zkcgU_I$ezDGN`wY8q#9En+cvu+j7Z7oZq{DU})bijLC88XUt=4WWcG^oXBGi zfX?+B=v=?S@x%Q7AU8ktrER&9-@5tq#d|n+`F$Ke>uf$N-XSqPA@dU#2TEo%^p3RuSNASD6!L zI!$b-4}_rgoDSRaqkr`Oo#R^((VEd(<^eFGXK|KAUo0XZi5rQ~&MmKV;dPI4@l8)~ z{!%SBby{2wUu&o5Y=1;J_Me~;ZlNG9T$ZKjGux+@;q#jQvMqzbd^Qr02wbc`dm`uIq|aeJ`~}s zjsSgplUss3M<4r8WwMw9`aXfOAqX7jh9>VY2BH6RmDOq$7>^6%B^MNVN?fMb0Lt~& z?h-^pr8alFfRl?W%QJf7`tEZ9<%DjR2k4`E;&#CM_tu3o8rNfY0z+1Xa@g&R^d-l_ z1L2DBLbAUqnTm|Y_1J3I--*quoLuh#Rlo_0s5JeXhL=jvck(#T4eu5eZeu0U`ifE#dk=UStAZ7*%ni%>M8W{Te0gZfQ zW=0BnE_6dNOz+Ol%qqeZT)L{d>h|sG*{#(ot0quQpqjv+PoR#16|j{9>gxk#nY28C z?*e$Fw=7Mo1$5XuXijCAgT(UY^~F<$IY?U3a83O@=mqUC2WeJ47u0qd5ncHGIiS9n zIX1HBd8aOXvlIAN;1gu&P1dOI5{!eSITFeGwD{aN4zRvt>`CLXxo3DZUu%xQ*2Mf> z-?Z-Qb;k{`kOQ0ad;y)c2CO-PmuJGn8@2u{W?p0*7QFX(_r&i^en*@?j3sX$t~Igy&uz4XJ7yKp+B}Z({q;K(C^W5 zof2D%=~0b-$ID>90p{3~rM*`N`46CPd>e#!(yDtXuYJ`8YosUCWLeYnsBaQXd4f?^ zeFq~xR$lwo+O5Z)bw{ALy0yuK|C@TGLwa;(%*xWU#{oL)6?8`q(>v*V9FFYwkbeW> z3IDT^(x&NID#Q~s>7K1ZcL^NR_(kmyg1}c~Jt!)~^v~ey?M`w)Z&b!ypsB2yKsAB? GIDsDoo; + + + + + + + + + + + + diff --git a/crates/silkscreen-android/src/codec.rs b/crates/silkscreen-android/src/codec.rs index 0cec330..536cfad 100644 --- a/crates/silkscreen-android/src/codec.rs +++ b/crates/silkscreen-android/src/codec.rs @@ -24,6 +24,8 @@ fn mime(codec: Codec) -> &'static str { Codec::H264 => "video/avc", Codec::Hevc => "video/hevc", Codec::Av1 => "video/av01", + // Not advertised by this app; MediaCodec refuses it if asked. + Codec::PyroWave => "video/x-pyrowave", } } diff --git a/crates/silkscreen-android/src/nal.rs b/crates/silkscreen-android/src/nal.rs index d68e1f3..ad3a0fa 100644 --- a/crates/silkscreen-android/src/nal.rs +++ b/crates/silkscreen-android/src/nal.rs @@ -9,7 +9,7 @@ pub fn parameter_sets(codec: Codec, au: &[u8]) -> Option<&[u8]> { let is_param = |nal: u8| match codec { Codec::H264 => matches!(nal & 0x1f, 7 | 8), Codec::Hevc => matches!((nal >> 1) & 0x3f, 32..=34), - Codec::Av1 => false, + Codec::Av1 | Codec::PyroWave => false, }; let mut end = 0; let mut i = 0; diff --git a/crates/silkscreen-codec/Cargo.toml b/crates/silkscreen-codec/Cargo.toml index 95f0936..3648e0b 100644 --- a/crates/silkscreen-codec/Cargo.toml +++ b/crates/silkscreen-codec/Cargo.toml @@ -11,3 +11,14 @@ ffmpeg-sys-next = "9.0.1" rayon = "1.12.0" silkscreen-proto = { path = "../silkscreen-proto" } tracing = "0.1.44" +# Loads the PyroWave library on first use (not on macOS, where it is linked in). +libloading = { version = "0.9.0", optional = true } + +[features] +# PyroWave (experimental GPU codec), built from third_party/pyrowave: see +# build.rs. Needs the submodules, a C++ compiler and (except on macOS) CMake. +pyrowave = ["dep:cc", "dep:cmake", "dep:libloading"] + +[build-dependencies] +cc = { version = "1.2", optional = true } +cmake = { version = "0.1.54", optional = true } diff --git a/crates/silkscreen-codec/build.rs b/crates/silkscreen-codec/build.rs index a38f970..d9eceb1 100644 --- a/crates/silkscreen-codec/build.rs +++ b/crates/silkscreen-codec/build.rs @@ -1,8 +1,122 @@ // Exposes the FFmpeg version ffmpeg-sys-next found, so the code can use // APIs that newer releases add (and older ones lack, or the reverse). +// With the `pyrowave` feature, also builds PyroWave. fn main() { println!("cargo::rustc-check-cfg=cfg(ffmpeg_7_1)"); if std::env::var("DEP_FFMPEG_FFMPEG_7_1").is_ok_and(|v| v == "true") { println!("cargo::rustc-cfg=ffmpeg_7_1"); } + #[cfg(feature = "pyrowave")] + pyrowave::build(); +} + +#[cfg(feature = "pyrowave")] +mod pyrowave { + use std::path::{Path, PathBuf}; + + pub fn build() { + let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("../.."); + let src = root.join("third_party/pyrowave"); + if !src.join("pyrowave.h").exists() { + panic!( + "PyroWave sources missing: run `git submodule update --init --recursive \ + third_party/pyrowave third_party/granite` (or build with \ + --no-default-features to leave PyroWave out)" + ); + } + println!("cargo::rerun-if-changed=build.rs"); + println!("cargo::rerun-if-changed=src/pyrowave_metal_shim.mm"); + println!( + "cargo::rerun-if-changed={}", + src.join("pyrowave.h").display() + ); + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") { + metal(&src.join("metal")); + } else { + vulkan(&src, &root.join("third_party/granite")); + } + } + + /// The Metal port, compiled straight into the binary. + fn metal(dir: &Path) { + cc::Build::new() + .cpp(true) + .std("c++14") + .include(dir) + .file(dir.join("pyrowave_bitstream.cpp")) + .warnings(false) + .compile("pyrowave_metal_cpp"); + cc::Build::new() + .cpp(true) + .std("c++14") + .flag("-fobjc-arc") + .include(dir) + .include(dir.join("shaders")) + .file(dir.join("pyrowave_common.mm")) + .file(dir.join("pyrowave_encoder.mm")) + .file(dir.join("pyrowave_decoder.mm")) + .file("src/pyrowave_metal_shim.mm") + .warnings(false) + .compile("pyrowave_metal"); + for fw in ["Metal", "IOSurface", "Foundation"] { + println!("cargo::rustc-link-lib=framework={fw}"); + } + println!("cargo::rustc-link-lib=objc"); + } + + /// The Vulkan library, built with CMake as a shared library and copied + /// next to the binaries, which load it when PyroWave is first used (not + /// linked: a missing or blocked library mustn't stop the app starting). + fn vulkan(src: &Path, granite: &Path) { + // PyroWave's CMake expects Granite inside its own tree (which its + // .gitignore already ignores). + let link = src.join("Granite"); + if !link.join("CMakeLists.txt").exists() { + // Also replaces a link made at another path (e.g. in a container). + let _ = std::fs::remove_file(&link); + let _ = std::fs::remove_dir(&link); + link_dir(granite, &link); + } + let dst = cmake::Config::new(src) + .define("PYROWAVE_DEVEL", "OFF") + .profile("Release") + .build(); + // target//, where the binaries end up. + let out = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + let profile_dir = out.ancestors().nth(3).unwrap().to_path_buf(); + for dir in ["bin", "lib"] { + let Ok(entries) = std::fs::read_dir(dst.join(dir)) else { + continue; + }; + for e in entries.flatten() { + let name = e.file_name().to_string_lossy().into_owned(); + let runtime = name.ends_with(".dll") || name.contains(".so"); + if name.contains("pyrowave-shared") && runtime { + for to in [profile_dir.clone(), profile_dir.join("deps")] { + let _ = std::fs::create_dir_all(&to); + std::fs::copy(e.path(), to.join(&name)) + .unwrap_or_else(|err| panic!("copying {name}: {err}")); + } + } + } + } + } + + /// Relative, so the link works wherever the checkout is mounted. + #[cfg(unix)] + fn link_dir(_target: &Path, link: &Path) { + std::os::unix::fs::symlink("../granite", link).expect("linking Granite into PyroWave"); + } + + /// A directory junction: unlike a symlink, needs no special rights. + #[cfg(windows)] + fn link_dir(target: &Path, link: &Path) { + let ok = std::process::Command::new("cmd") + .args(["/C", "mklink", "/J"]) + .arg(link) + .arg(target) + .status() + .is_ok_and(|s| s.success()); + assert!(ok, "linking Granite into PyroWave"); + } } diff --git a/crates/silkscreen-codec/src/decoder.rs b/crates/silkscreen-codec/src/decoder.rs index 2fa5634..60619cb 100644 --- a/crates/silkscreen-codec/src/decoder.rs +++ b/crates/silkscreen-codec/src/decoder.rs @@ -58,7 +58,8 @@ pub struct Picture<'a> { pub planes: Vec<(&'a [u8], usize)>, } -pub struct Decoder { +/// An FFmpeg decoder. +struct FfDecoder { ctx: CodecCtx, hw_pix_fmt: Option, pkt: Packet, @@ -69,15 +70,16 @@ pub struct Decoder { name: String, } -unsafe impl Send for Decoder {} +unsafe impl Send for FfDecoder {} -impl Decoder { - pub fn open(codec: Codec, allow_hw: bool) -> Result { +impl FfDecoder { + fn open(codec: Codec, allow_hw: bool) -> Result { init_logging(); let id = match codec { Codec::H264 => ff::AVCodecID::AV_CODEC_ID_H264, Codec::Hevc => ff::AVCodecID::AV_CODEC_ID_HEVC, Codec::Av1 => ff::AVCodecID::AV_CODEC_ID_AV1, + Codec::PyroWave => bail!("PyroWave is not an FFmpeg codec"), }; unsafe { // FFmpeg's default AV1 decoder is libdav1d (software only); the @@ -114,6 +116,7 @@ impl Decoder { Codec::H264 => c"h264_v4l2m2m", Codec::Hevc => c"hevc_v4l2m2m", Codec::Av1 => c"av1_v4l2m2m", + Codec::PyroWave => unreachable!(), }; let m2m = ff::avcodec_find_decoder_by_name(name.as_ptr()); if !m2m.is_null() { @@ -168,7 +171,7 @@ impl Decoder { Some(kind) => format!("{} ({})", cstr((*dec).name), hw_device_name(kind)), None => format!("{} (software)", cstr((*dec).name)), }; - Ok(Decoder { + Ok(FfDecoder { ctx, hw_pix_fmt, pkt: Packet::new()?, @@ -302,15 +305,65 @@ pub fn decodable_codecs() -> Vec { } else { [Codec::Hevc, Codec::H264, Codec::Av1] }; - order + let mut codecs: Vec<_> = order .into_iter() .filter(|c| { let id = match c { Codec::H264 => ff::AVCodecID::AV_CODEC_ID_H264, Codec::Hevc => ff::AVCodecID::AV_CODEC_ID_HEVC, Codec::Av1 => ff::AVCodecID::AV_CODEC_ID_AV1, + Codec::PyroWave => unreachable!(), }; !unsafe { ff::avcodec_find_decoder(id) }.is_null() }) - .collect() + .collect(); + // Last: experimental, and only used when the host picks it. + #[cfg(feature = "pyrowave")] + if crate::pyrowave::available() { + codecs.push(Codec::PyroWave); + } + codecs +} + +/// A video decoder: FFmpeg's, or PyroWave. +pub struct Decoder(Backend); + +enum Backend { + Ff(FfDecoder), + #[cfg(feature = "pyrowave")] + PyroWave(crate::pyrowave::PyroDecoder), +} + +impl Decoder { + /// Opens a decoder, in hardware when `allow_hw` and possible. PyroWave + /// always runs on the GPU. + pub fn open(codec: Codec, allow_hw: bool) -> Result { + if codec == Codec::PyroWave { + #[cfg(feature = "pyrowave")] + return Ok(Decoder(Backend::PyroWave( + crate::pyrowave::PyroDecoder::open()?, + ))); + #[cfg(not(feature = "pyrowave"))] + bail!("this build of Silkscreen has no PyroWave"); + } + Ok(Decoder(Backend::Ff(FfDecoder::open(codec, allow_hw)?))) + } + + pub fn name(&self) -> &str { + match &self.0 { + Backend::Ff(d) => d.name(), + #[cfg(feature = "pyrowave")] + Backend::PyroWave(d) => d.name(), + } + } + + /// Feeds one complete access unit and returns the newest decoded picture, + /// if this packet produced one. + pub fn decode(&mut self, data: &[u8]) -> Result>> { + match &mut self.0 { + Backend::Ff(d) => d.decode(data), + #[cfg(feature = "pyrowave")] + Backend::PyroWave(d) => d.decode(data), + } + } } diff --git a/crates/silkscreen-codec/src/encoder.rs b/crates/silkscreen-codec/src/encoder.rs index c2bd3f8..3a01a7f 100644 --- a/crates/silkscreen-codec/src/encoder.rs +++ b/crates/silkscreen-codec/src/encoder.rs @@ -47,6 +47,7 @@ pub fn candidates(codec: Codec) -> Vec<&'static str> { Codec::Hevc => &["hevc_videotoolbox"], #[cfg(target_os = "macos")] Codec::Av1 => &[], + Codec::PyroWave => &[], #[cfg(not(any(target_os = "linux", windows, target_os = "macos")))] _ => &[], }; @@ -54,6 +55,7 @@ pub fn candidates(codec: Codec) -> Vec<&'static str> { Codec::H264 => &["libx264", "libopenh264"], Codec::Hevc => &["libx265"], Codec::Av1 => &["libsvtav1"], + Codec::PyroWave => &[], }; hw.iter().chain(sw).copied().collect() } @@ -118,7 +120,8 @@ enum Input { Upload { frames: BufRef, hw_frame: Frame }, } -pub struct Encoder { +/// An FFmpeg encoder. +struct FfEncoder { ctx: CodecCtx, name: String, chroma: ChromaLayout, @@ -130,10 +133,10 @@ pub struct Encoder { } // Each encoder is driven by a single thread at a time. -unsafe impl Send for Encoder {} +unsafe impl Send for FfEncoder {} -impl Encoder { - pub fn open(cfg: &EncoderConfig) -> Result { +impl FfEncoder { + fn open(cfg: &EncoderConfig) -> Result { init_logging(); if !cfg.width.is_multiple_of(2) || !cfg.height.is_multiple_of(2) { bail!( @@ -275,7 +278,7 @@ impl Encoder { f.color_trc = c.color_trc; check(ff::av_frame_get_buffer(frame.0, 64), "av_frame_get_buffer")?; - Ok(Encoder { + Ok(FfEncoder { ctx, name: name.to_owned(), chroma, @@ -465,6 +468,13 @@ unsafe fn supported_pix_fmts(codec: *const ff::AVCodec) -> Vec { /// Names of every encoder in [`candidates`] that this FFmpeg build contains /// (not necessarily usable on this machine's hardware). pub fn compiled_encoders(codec: Codec) -> Vec<&'static str> { + if codec == Codec::PyroWave { + #[cfg(feature = "pyrowave")] + if crate::pyrowave::available() { + return vec![crate::pyrowave::ENCODER_NAME]; + } + return Vec::new(); + } candidates(codec) .into_iter() .filter(|n| { @@ -489,8 +499,86 @@ pub fn default_bitrate_kbps(codec: Codec, width: u32, height: u32, refresh_mhz: Codec::H264 => 0.10, Codec::Hevc => 0.07, Codec::Av1 => 0.06, + #[cfg(feature = "pyrowave")] + Codec::PyroWave => { + return crate::pyrowave::default_bitrate_kbps(width, height, refresh_mhz); + } + #[cfg(not(feature = "pyrowave"))] + Codec::PyroWave => 1.0, }; let fps = refresh_mhz as f64 / 1000.0; let kbps = width as f64 * height as f64 * fps * bits_per_pixel / 1000.0; (kbps as u32).clamp(5_000, 200_000) } + +/// A video encoder: FFmpeg's, or PyroWave. +pub struct Encoder(Backend); + +enum Backend { + Ff(FfEncoder), + #[cfg(feature = "pyrowave")] + PyroWave(crate::pyrowave::PyroEncoder), +} + +impl Encoder { + pub fn open(cfg: &EncoderConfig) -> Result { + if cfg.codec == Codec::PyroWave { + #[cfg(feature = "pyrowave")] + return Ok(Encoder(Backend::PyroWave( + crate::pyrowave::PyroEncoder::open(cfg)?, + ))); + #[cfg(not(feature = "pyrowave"))] + bail!("this build of Silkscreen has no PyroWave"); + } + Ok(Encoder(Backend::Ff(FfEncoder::open(cfg)?))) + } + + pub fn name(&self) -> &str { + match &self.0 { + Backend::Ff(e) => e.name(), + #[cfg(feature = "pyrowave")] + Backend::PyroWave(e) => e.name(), + } + } + + /// Encodes one captured frame. `emit` is called for each output packet + /// with `(bitstream, is_keyframe)`; with the low-latency settings that + /// is exactly once per input frame. + pub fn encode( + &mut self, + src: &RgbFrame, + pts_us: u64, + force_keyframe: bool, + emit: impl FnMut(&[u8], bool), + ) -> Result<()> { + match &mut self.0 { + Backend::Ff(e) => e.encode(src, pts_us, force_keyframe, emit), + #[cfg(feature = "pyrowave")] + Backend::PyroWave(e) => e.encode(src, emit), + } + } + + /// Re-encodes the previous frame. Used to answer a keyframe request when + /// the screen is static and no new frame is coming. + pub fn encode_again( + &mut self, + pts_us: u64, + force_keyframe: bool, + emit: impl FnMut(&[u8], bool), + ) -> Result<()> { + match &mut self.0 { + Backend::Ff(e) => e.encode_again(pts_us, force_keyframe, emit), + #[cfg(feature = "pyrowave")] + Backend::PyroWave(e) => e.encode_again(emit), + } + } + + /// Emits packets an asynchronous encoder finished since the last call. + pub fn poll(&mut self, emit: impl FnMut(&[u8], bool)) -> Result<()> { + match &mut self.0 { + Backend::Ff(e) => e.poll(emit), + #[cfg(feature = "pyrowave")] + Backend::PyroWave(_) => Ok(()), + } + } +} diff --git a/crates/silkscreen-codec/src/lib.rs b/crates/silkscreen-codec/src/lib.rs index 0b15ec0..6c43419 100644 --- a/crates/silkscreen-codec/src/lib.rs +++ b/crates/silkscreen-codec/src/lib.rs @@ -6,6 +6,8 @@ pub mod convert; pub mod decoder; pub mod encoder; mod ff; +#[cfg(feature = "pyrowave")] +pub mod pyrowave; pub use convert::{RgbFrame, RgbLayout}; pub use decoder::{Decoder, Matrix, Picture, PictureFormat, decodable_codecs}; diff --git a/crates/silkscreen-codec/src/pyrowave.rs b/crates/silkscreen-codec/src/pyrowave.rs new file mode 100644 index 0000000..036ce54 --- /dev/null +++ b/crates/silkscreen-codec/src/pyrowave.rs @@ -0,0 +1,729 @@ +//! PyroWave (experimental): Hans-Kristian Arntzen's intra-only wavelet codec, +//! run in GPU compute shaders — Vulkan on Windows and Linux, a native Metal +//! port on Apple Silicon Macs. Every frame is a keyframe, encode and decode +//! take well under a millisecond, and the price is bandwidth: it is meant +//! for a few hundred Mbit/s on Ethernet. +//! +//! Frames go through the CPU on both sides: BGRX is converted to YUV 4:2:0 +//! (BT.709, limited range, as for the FFmpeg encoders) and uploaded; the +//! display reads the decoded planes back into a [`Picture`]. +//! +//! On the wire each frame is +//! `[width u16][height u16][packet count u32][packet sizes u32…][packets…]` +//! (little endian): PyroWave's own packets are decoded one by one, and the +//! size lets the display create its decoder without a separate handshake. + +use std::ffi::{c_int, c_void}; +use std::sync::OnceLock; + +use anyhow::{Context, Result, bail, ensure}; +use tracing::{info, warn}; + +use crate::convert::{ChromaLayout, RgbFrame, YuvPlanes, rgb_to_yuv420}; +use crate::decoder::{Matrix, Picture, PictureFormat}; +use crate::encoder::EncoderConfig; + +mod ffi { + use super::*; + + pub type Device = *mut c_void; + pub type Encoder = *mut c_void; + pub type Decoder = *mut c_void; + pub type Result = c_int; + pub const SUCCESS: Result = 0; + pub const CHROMA_420: c_int = 0; + pub const CPU_YUV420P: c_int = 1; + + #[repr(C)] + pub struct EncoderCreateInfo { + pub device: Device, + pub width: c_int, + pub height: c_int, + pub chroma: c_int, + } + + #[repr(C)] + pub struct DecoderCreateInfo { + pub device: Device, + pub width: c_int, + pub height: c_int, + pub chroma: c_int, + /// Vulkan only: the fragment-shader path for weak GPUs. + #[cfg(not(target_os = "macos"))] + pub fragment_path: bool, + } + + #[repr(C)] + #[derive(Clone, Copy, Default)] + pub struct Packet { + pub offset: usize, + pub size: usize, + } + + #[repr(C)] + pub struct RateControl { + pub maximum_bitstream_size: usize, + } + + #[repr(C)] + pub struct CpuBuffer { + pub data: [*mut c_void; 3], + pub row_stride_in_bytes: [usize; 3], + pub plane_size_in_bytes: [usize; 3], + pub width: c_int, + pub height: c_int, + pub format: c_int, + } + + /// Declares PyroWave's C functions. On macOS the Metal port is linked + /// in. Elsewhere the library is loaded when PyroWave is first used, so a + /// missing or blocked DLL (Windows Smart App Control refuses unsigned + /// ones it doesn't know) only makes PyroWave unavailable instead of + /// stopping the app from starting. + macro_rules! api { + ($(fn $name:ident($($arg:ident: $ty:ty),* $(,)?) $(-> $ret:ty)?;)*) => { + // (The Vulkan-only ones go unused here.) + #[cfg(target_os = "macos")] + #[allow(dead_code)] + unsafe extern "C" { + $(pub fn $name($($arg: $ty),*) $(-> $ret)?;)* + } + + #[cfg(not(target_os = "macos"))] + pub struct Api { + $($name: unsafe extern "C" fn($($ty),*) $(-> $ret)?,)* + _lib: libloading::Library, + } + + #[cfg(not(target_os = "macos"))] + impl Api { + pub fn load(lib: libloading::Library) -> std::result::Result { + unsafe { + Ok(Api { + $($name: *lib.get(concat!(stringify!($name), "\0").as_bytes())?,)* + _lib: lib, + }) + } + } + } + + $( + #[cfg(not(target_os = "macos"))] + pub unsafe fn $name($($arg: $ty),*) $(-> $ret)? { + unsafe { (api().expect("PyroWave library not loaded").$name)($($arg),*) } + } + )* + }; + } + + api! { + fn pyrowave_create_default_device(device: *mut Device) -> Result; + fn pyrowave_encoder_create(info: *const EncoderCreateInfo, enc: *mut Encoder) -> Result; + fn pyrowave_encoder_encode_cpu( + enc: Encoder, + input: *const CpuBuffer, + rc: *const RateControl, + ) -> Result; + fn pyrowave_encoder_compute_num_packets( + enc: Encoder, + boundary: usize, + num: *mut usize, + ) -> Result; + fn pyrowave_encoder_packetize( + enc: Encoder, + packets: *mut Packet, + boundary: usize, + out_packets: *mut usize, + bitstream: *mut c_void, + size: usize, + ) -> Result; + fn pyrowave_encoder_destroy(enc: Encoder); + fn pyrowave_decoder_create(info: *const DecoderCreateInfo, dec: *mut Decoder) -> Result; + fn pyrowave_decoder_clear(dec: Decoder); + fn pyrowave_decoder_push_packet(dec: Decoder, data: *const c_void, size: usize) -> Result; + fn pyrowave_decoder_decode_is_ready(dec: Decoder, allow_partial: bool) -> bool; + fn pyrowave_decoder_destroy(dec: Decoder); + // Vulkan library only (the Metal port decodes through the shim below). + fn pyrowave_decoder_device_prefers_fragment_path(device: Device) -> bool; + fn pyrowave_decoder_decode_cpu_buffer_synchronous( + dec: Decoder, + out: *const CpuBuffer, + ) -> Result; + } + + // The Metal port decodes into textures only; this shim (pyrowave_metal_shim.mm) + // decodes into shared textures and copies them out. + #[cfg(target_os = "macos")] + unsafe extern "C" { + pub fn pyrowave_result_to_string(r: Result) -> *const std::ffi::c_char; + pub fn silkscreen_pyrowave_readback_new(width: c_int, height: c_int) -> *mut c_void; + pub fn silkscreen_pyrowave_readback_decode( + readback: *mut c_void, + dec: Decoder, + out: *const CpuBuffer, + ) -> Result; + pub fn silkscreen_pyrowave_readback_free(readback: *mut c_void); + } +} + +/// Loads the PyroWave library (next to the executable first), once. +#[cfg(not(target_os = "macos"))] +fn api() -> Option<&'static ffi::Api> { + static API: OnceLock> = OnceLock::new(); + API.get_or_init(|| { + let names: &[&str] = if cfg!(windows) { + &[ + "libpyrowave-shared-1.dll", + "pyrowave-shared-1.dll", + "pyrowave-shared.dll", + ] + } else { + &["libpyrowave-shared.so.0", "libpyrowave-shared.so"] + }; + let dir = std::env::current_exe() + .ok() + .and_then(|e| e.parent().map(|d| d.to_path_buf())); + let mut errors = Vec::new(); + for name in names { + let paths = dir.iter().map(|d| d.join(name)).chain([name.into()]); + for path in paths { + match unsafe { libloading::Library::new(&path) }.and_then(ffi::Api::load) { + Ok(api) => { + info!("loaded {}", path.display()); + return Some(api); + } + Err(e) => errors.push(format!("{}: {e}", path.display())), + } + } + } + warn!("PyroWave library not loaded:\n{}", errors.join("\n")); + None + }) + .as_ref() +} + +fn check(r: ffi::Result, what: &str) -> Result<()> { + if r == ffi::SUCCESS { + return Ok(()); + } + #[cfg(target_os = "macos")] + let msg = unsafe { std::ffi::CStr::from_ptr(ffi::pyrowave_result_to_string(r)) } + .to_string_lossy() + .into_owned(); + #[cfg(not(target_os = "macos"))] + let msg = format!("error {r}"); + bail!("{what}: {msg}") +} + +/// GPU backend name, for logs and the UI. +const BACKEND: &str = if cfg!(target_os = "macos") { + "Metal" +} else { + "Vulkan" +}; + +/// PyroWave's packets, whose size it treats as a guideline (oversized +/// blocks make bigger ones). Silkscreen splits frames into datagrams itself. +const PACKET_BOUNDARY: usize = 16 * 1024; + +struct Device(ffi::Device); +// The device is created once and only used to create encoders/decoders. +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + +static PROBE: OnceLock bool + Send + Sync>> = OnceLock::new(); + +/// Has PyroWave tried out somewhere safe (the app runs a copy of itself) +/// before this process touches the GPU driver: a driver that crashes on it +/// then just leaves PyroWave unavailable instead of taking the app down. +pub fn set_probe(probe: impl Fn() -> bool + Send + Sync + 'static) { + let _ = PROBE.set(Box::new(probe)); +} + +/// Encodes and decodes a small frame here; what the separate probe runs. +pub fn probe_here() -> bool { + let r = (|| -> Result<()> { + let (w, h) = (64u32, 64u32); + let rgb = vec![128u8; (w * h * 4) as usize]; + let src = RgbFrame { + width: w, + height: h, + stride: (w * 4) as usize, + layout: crate::convert::RgbLayout::Bgrx, + data: &rgb, + }; + let cfg = EncoderConfig { + codec: silkscreen_proto::Codec::PyroWave, + width: w, + height: h, + refresh_mhz: 60_000, + bitrate_kbps: default_bitrate_kbps(w, h, 60_000), + encoder: None, + }; + let mut frame = Vec::new(); + PyroEncoder::open(&cfg)?.encode(&src, |d, _| frame = d.to_vec())?; + let mut dec = PyroDecoder::open()?; + dec.decode(&frame)?.context("no picture")?; + Ok(()) + })(); + if let Err(e) = &r { + warn!("PyroWave check failed: {e:#}"); + } + r.is_ok() +} + +/// The shared GPU device, created on first use (that compiles the shaders), +/// or `None` if this machine can't run PyroWave. +fn device() -> Option { + static DEVICE: OnceLock> = OnceLock::new(); + DEVICE + .get_or_init(|| { + if let Some(probe) = PROBE.get() + && !probe() + { + warn!("PyroWave failed its check on this GPU; leaving it off"); + return None; + } + #[cfg(not(target_os = "macos"))] + api()?; + let mut dev = std::ptr::null_mut(); + match check( + unsafe { ffi::pyrowave_create_default_device(&mut dev) }, + "creating the PyroWave device", + ) { + Ok(()) => { + info!("PyroWave available ({BACKEND})"); + Some(Device(dev)) + } + Err(e) => { + warn!("PyroWave unavailable: {e:#}"); + None + } + } + }) + .as_ref() + .map(|d| d.0) +} + +/// Whether this machine's GPU can run PyroWave. +pub fn available() -> bool { + device().is_some() +} + +/// Encoder name shown in the UI and `doctor`. +pub const ENCODER_NAME: &str = "pyrowave"; + +/// Default bitrate: about 1 bit per pixel, which PyroWave needs to look clean. +pub fn default_bitrate_kbps(width: u32, height: u32, refresh_mhz: u32) -> u32 { + let fps = refresh_mhz as f64 / 1000.0; + let kbps = width as f64 * height as f64 * fps / 1000.0; + (kbps as u32).clamp(50_000, 900_000) +} + +/// Planar YUV 4:2:0 in one allocation, in the layout PyroWave's CPU calls use. +struct Planes { + data: Vec, + width: usize, + height: usize, +} + +impl Planes { + fn new(width: usize, height: usize) -> Self { + Planes { + data: vec![0; width * height * 3 / 2], + width, + height, + } + } + + fn split(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) { + let y = self.width * self.height; + let c = y / 4; + let (ya, rest) = self.data.split_at_mut(y); + let (u, v) = rest.split_at_mut(c); + (ya, u, &mut v[..c]) + } + + fn cpu_buffer(&mut self) -> ffi::CpuBuffer { + let (w, h) = (self.width, self.height); + let (y, u, v) = self.split(); + ffi::CpuBuffer { + data: [ + y.as_mut_ptr().cast(), + u.as_mut_ptr().cast(), + v.as_mut_ptr().cast(), + ], + row_stride_in_bytes: [w, w / 2, w / 2], + plane_size_in_bytes: [w * h, w * h / 4, w * h / 4], + width: w as c_int, + height: h as c_int, + format: ffi::CPU_YUV420P, + } + } +} + +pub struct PyroEncoder { + enc: ffi::Encoder, + planes: Planes, + budget: usize, + bitstream: Vec, + packets: Vec, + /// The last frame as sent, for [`PyroEncoder::encode_again`]. + frame: Vec, +} + +// Driven by one thread at a time, like the FFmpeg encoders. +unsafe impl Send for PyroEncoder {} + +impl PyroEncoder { + pub fn open(cfg: &EncoderConfig) -> Result { + ensure!( + cfg.width.is_multiple_of(2) && cfg.height.is_multiple_of(2), + "encoder dimensions must be even, got {}x{}", + cfg.width, + cfg.height + ); + ensure!( + cfg.width <= 16384 && cfg.height <= 16384, + "PyroWave frames are at most 16384 pixels across" + ); + let device = device().context("this GPU can't run PyroWave")?; + let info = ffi::EncoderCreateInfo { + device, + width: cfg.width as c_int, + height: cfg.height as c_int, + chroma: ffi::CHROMA_420, + }; + let mut enc = std::ptr::null_mut(); + check( + unsafe { ffi::pyrowave_encoder_create(&info, &mut enc) }, + "creating the PyroWave encoder", + )?; + let fps = (cfg.refresh_mhz.max(1000) as f64) / 1000.0; + let budget = ((cfg.bitrate_kbps as f64 * 1000.0 / 8.0 / fps) as usize).max(16 * 1024); + info!( + "opened {}x{} PyroWave encoder ({BACKEND}), {} KB per frame", + cfg.width, + cfg.height, + budget / 1000 + ); + Ok(PyroEncoder { + enc, + planes: Planes::new(cfg.width as usize, cfg.height as usize), + budget, + // The rate control is exact, plus some headroom for packet headers. + bitstream: vec![0; budget * 2 + 64 * 1024], + packets: Vec::new(), + frame: Vec::new(), + }) + } + + pub fn name(&self) -> &str { + if cfg!(target_os = "macos") { + "pyrowave (Metal)" + } else { + "pyrowave (Vulkan)" + } + } + + /// Encodes one frame; `emit` gets it at once (always a keyframe). + pub fn encode(&mut self, src: &RgbFrame, mut emit: impl FnMut(&[u8], bool)) -> Result<()> { + let (w, h) = (self.planes.width, self.planes.height); + ensure!( + src.width as usize == w && src.height as usize == h, + "frame size {}x{} does not match encoder {w}x{h}", + src.width, + src.height + ); + { + let (y, u, v) = self.planes.split(); + let dst = YuvPlanes { + y, + y_stride: w, + u, + u_stride: w / 2, + v: Some((v, w / 2)), + }; + rgb_to_yuv420(src, dst, ChromaLayout::I420); + } + let input = self.planes.cpu_buffer(); + let rc = ffi::RateControl { + maximum_bitstream_size: self.budget, + }; + unsafe { + check( + ffi::pyrowave_encoder_encode_cpu(self.enc, &input, &rc), + "PyroWave encode", + )?; + let mut n = 0; + check( + ffi::pyrowave_encoder_compute_num_packets(self.enc, PACKET_BOUNDARY, &mut n), + "PyroWave packet count", + )?; + self.packets.resize(n, ffi::Packet::default()); + let mut out = 0; + check( + ffi::pyrowave_encoder_packetize( + self.enc, + self.packets.as_mut_ptr(), + PACKET_BOUNDARY, + &mut out, + self.bitstream.as_mut_ptr().cast(), + self.bitstream.len(), + ), + "PyroWave packetize", + )?; + self.packets.truncate(out); + } + let f = &mut self.frame; + f.clear(); + f.extend_from_slice(&(w as u16).to_le_bytes()); + f.extend_from_slice(&(h as u16).to_le_bytes()); + f.extend_from_slice(&(self.packets.len() as u32).to_le_bytes()); + for p in &self.packets { + f.extend_from_slice(&(p.size as u32).to_le_bytes()); + } + for p in &self.packets { + f.extend_from_slice(&self.bitstream[p.offset..p.offset + p.size]); + } + emit(&self.frame, true); + Ok(()) + } + + /// Sends the last frame again (it is a keyframe anyway). + pub fn encode_again(&mut self, mut emit: impl FnMut(&[u8], bool)) -> Result<()> { + if !self.frame.is_empty() { + emit(&self.frame, true); + } + Ok(()) + } +} + +impl Drop for PyroEncoder { + fn drop(&mut self) { + unsafe { ffi::pyrowave_encoder_destroy(self.enc) }; + } +} + +/// A decoder for one frame size. +struct Sized { + dec: ffi::Decoder, + planes: Planes, + #[cfg(target_os = "macos")] + readback: *mut c_void, +} + +impl Drop for Sized { + fn drop(&mut self) { + unsafe { + #[cfg(target_os = "macos")] + ffi::silkscreen_pyrowave_readback_free(self.readback); + ffi::pyrowave_decoder_destroy(self.dec); + } + } +} + +pub struct PyroDecoder { + /// Created from the first frame's size, recreated if it changes. + sized: Option, +} + +unsafe impl Send for PyroDecoder {} + +impl PyroDecoder { + pub fn open() -> Result { + device().context("this GPU can't run PyroWave")?; + Ok(PyroDecoder { sized: None }) + } + + pub fn name(&self) -> &str { + if cfg!(target_os = "macos") { + "pyrowave (Metal)" + } else { + "pyrowave (Vulkan)" + } + } + + fn sized(&mut self, width: usize, height: usize) -> Result<&mut Sized> { + if self + .sized + .as_ref() + .is_some_and(|s| (s.planes.width, s.planes.height) == (width, height)) + { + return Ok(self.sized.as_mut().unwrap()); + } + self.sized = None; + let device = device().context("no PyroWave device")?; + let info = ffi::DecoderCreateInfo { + device, + width: width as c_int, + height: height as c_int, + chroma: ffi::CHROMA_420, + #[cfg(not(target_os = "macos"))] + fragment_path: unsafe { ffi::pyrowave_decoder_device_prefers_fragment_path(device) }, + }; + let mut dec = std::ptr::null_mut(); + check( + unsafe { ffi::pyrowave_decoder_create(&info, &mut dec) }, + "creating the PyroWave decoder", + )?; + #[cfg(target_os = "macos")] + let readback = + unsafe { ffi::silkscreen_pyrowave_readback_new(width as c_int, height as c_int) }; + #[cfg(target_os = "macos")] + if readback.is_null() { + unsafe { ffi::pyrowave_decoder_destroy(dec) }; + bail!("cannot create Metal textures for PyroWave"); + } + info!("opened {width}x{height} PyroWave decoder ({BACKEND})"); + Ok(self.sized.insert(Sized { + dec, + planes: Planes::new(width, height), + #[cfg(target_os = "macos")] + readback, + })) + } + + pub fn decode(&mut self, data: &[u8]) -> Result>> { + let (width, height, packets) = parse_frame(data)?; + let s = self.sized(width, height)?; + unsafe { + ffi::pyrowave_decoder_clear(s.dec); + for p in packets { + check( + ffi::pyrowave_decoder_push_packet(s.dec, p.as_ptr().cast(), p.len()), + "PyroWave push packet", + )?; + } + if !ffi::pyrowave_decoder_decode_is_ready(s.dec, false) { + bail!("incomplete PyroWave frame"); + } + let out = s.planes.cpu_buffer(); + #[cfg(target_os = "macos")] + let r = ffi::silkscreen_pyrowave_readback_decode(s.readback, s.dec, &out); + #[cfg(not(target_os = "macos"))] + let r = ffi::pyrowave_decoder_decode_cpu_buffer_synchronous(s.dec, &out); + check(r, "PyroWave decode")?; + } + let (w, h) = (width, height); + let (y, u, v) = s.planes.split(); + Ok(Some(Picture { + format: PictureFormat::I420, + width: w as u32, + height: h as u32, + matrix: Matrix::Bt709, + full_range: false, + planes: vec![(&*y, w), (&*u, w / 2), (&*v, w / 2)], + })) + } +} + +/// Splits a frame into its size and PyroWave packets. +fn parse_frame(data: &[u8]) -> Result<(usize, usize, Vec<&[u8]>)> { + let u32_at = |i: usize| -> Result { + let b = data.get(i..i + 4).context("truncated PyroWave frame")?; + Ok(u32::from_le_bytes(b.try_into().unwrap()) as usize) + }; + ensure!(data.len() >= 8, "truncated PyroWave frame"); + let width = u16::from_le_bytes([data[0], data[1]]) as usize; + let height = u16::from_le_bytes([data[2], data[3]]) as usize; + ensure!( + width > 0 && height > 0 && width.is_multiple_of(2) && height.is_multiple_of(2), + "bad PyroWave frame size {width}x{height}" + ); + let n = u32_at(4)?; + ensure!(n <= data.len() / 4, "bad PyroWave packet count {n}"); + let mut at = 8 + 4 * n; + let mut packets = Vec::with_capacity(n); + for i in 0..n { + let size = u32_at(8 + 4 * i)?; + let p = data + .get(at..at + size) + .context("truncated PyroWave packet")?; + packets.push(p); + at += size; + } + Ok((width, height, packets)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::convert::RgbLayout; + use silkscreen_proto::Codec; + + #[test] + fn rejects_garbage() { + assert!(parse_frame(&[]).is_err()); + assert!(parse_frame(&[2, 0, 2, 0, 255, 255, 255, 255]).is_err()); + assert!(parse_frame(&[2, 0, 2, 0, 1, 0, 0, 0, 9, 0, 0, 0, 1]).is_err()); + } + + /// Encodes and decodes a gradient on this machine's GPU; skipped where + /// PyroWave can't run (no Vulkan driver, Intel Mac, CI runners). + #[test] + fn roundtrip() { + if !available() { + eprintln!("PyroWave unavailable here; skipping"); + return; + } + let (w, h) = (640u32, 360u32); + let mut rgb = vec![0u8; (w * h * 4) as usize]; + for y in 0..h { + for x in 0..w { + let i = ((y * w + x) * 4) as usize; + rgb[i] = (x * 255 / w) as u8; + rgb[i + 1] = (y * 255 / h) as u8; + rgb[i + 2] = 128; + } + } + let src = RgbFrame { + width: w, + height: h, + stride: (w * 4) as usize, + layout: RgbLayout::Bgrx, + data: &rgb, + }; + let cfg = EncoderConfig { + codec: Codec::PyroWave, + width: w, + height: h, + refresh_mhz: 60_000, + bitrate_kbps: default_bitrate_kbps(w, h, 60_000), + encoder: None, + }; + let mut enc = PyroEncoder::open(&cfg).unwrap(); + let mut frame = Vec::new(); + enc.encode(&src, |d, key| { + assert!(key); + frame = d.to_vec(); + }) + .unwrap(); + let mut dec = PyroDecoder::open().unwrap(); + let pic = dec.decode(&frame).unwrap().unwrap(); + assert_eq!((pic.width, pic.height), (w, h)); + // Compare luma against the converter's own output. + let mut expect = Planes::new(w as usize, h as usize); + { + let (y, u, v) = expect.split(); + let dst = YuvPlanes { + y, + y_stride: w as usize, + u, + u_stride: w as usize / 2, + v: Some((v, w as usize / 2)), + }; + rgb_to_yuv420(&src, dst, ChromaLayout::I420); + } + let (ey, _, _) = expect.split(); + let mse = pic.planes[0] + .0 + .iter() + .zip(ey.iter()) + .map(|(a, b)| (*a as f64 - *b as f64).powi(2)) + .sum::() + / ey.len() as f64; + let psnr = 10.0 * (255.0f64 * 255.0 / mse.max(1e-9)).log10(); + assert!(psnr > 35.0, "luma PSNR {psnr:.1} dB"); + } +} diff --git a/crates/silkscreen-codec/src/pyrowave_metal_shim.mm b/crates/silkscreen-codec/src/pyrowave_metal_shim.mm new file mode 100644 index 0000000..b3820f5 --- /dev/null +++ b/crates/silkscreen-codec/src/pyrowave_metal_shim.mm @@ -0,0 +1,91 @@ +// The PyroWave Metal port decodes into textures only. This decodes into +// three shared-storage textures and copies the planes out, giving the Rust +// side the same CPU interface as the Vulkan library's +// pyrowave_decoder_decode_cpu_buffer_synchronous. + +#import +#include "pyrowave_metal.h" + +namespace +{ +struct Readback +{ + id device; + id queue; + id planes[3]; + int width; + int height; +}; +} + +extern "C" void *silkscreen_pyrowave_readback_new(int width, int height) +{ + @autoreleasepool + { + // The same device pyrowave_create_default_device picks. + id device = MTLCreateSystemDefaultDevice(); + if (!device) + return nullptr; + auto *r = new Readback(); + r->device = device; + r->queue = [device newCommandQueue]; + r->width = width; + r->height = height; + for (int p = 0; p < 3; p++) + { + int w = p ? width / 2 : width; + int h = p ? height / 2 : height; + MTLTextureDescriptor *d = + [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm + width:w + height:h + mipmapped:NO]; + d.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead; + d.storageMode = MTLStorageModeShared; + r->planes[p] = [device newTextureWithDescriptor:d]; + if (!r->planes[p] || !r->queue) + { + delete r; + return nullptr; + } + } + return r; + } +} + +extern "C" pyrowave_result silkscreen_pyrowave_readback_decode(void *readback, pyrowave_decoder decoder, + const pyrowave_cpu_buffer *out) +{ + @autoreleasepool + { + auto *r = static_cast(readback); + if (!r || !out || out->width != r->width || out->height != r->height) + return PYROWAVE_ERROR_INVALID_ARGUMENT; + id cb = [r->queue commandBuffer]; + pyrowave_gpu_buffers buffers = { + { (__bridge void *)r->planes[0], (__bridge void *)r->planes[1], (__bridge void *)r->planes[2] } + }; + pyrowave_result res = pyrowave_decoder_decode_gpu_buffer(decoder, (__bridge void *)cb, &buffers); + if (res != PYROWAVE_SUCCESS) + return res; + [cb commit]; + [cb waitUntilCompleted]; + if (cb.status != MTLCommandBufferStatusCompleted) + return PYROWAVE_ERROR_GENERIC; + for (int p = 0; p < 3; p++) + { + int w = p ? r->width / 2 : r->width; + int h = p ? r->height / 2 : r->height; + [r->planes[p] getBytes:out->data[p] + bytesPerRow:out->row_stride_in_bytes[p] + fromRegion:MTLRegionMake2D(0, 0, w, h) + mipmapLevel:0]; + } + return PYROWAVE_SUCCESS; + } +} + +extern "C" void silkscreen_pyrowave_readback_free(void *readback) +{ + delete static_cast(readback); +} diff --git a/crates/silkscreen-core/src/service.rs b/crates/silkscreen-core/src/service.rs index f1bde49..19d359d 100644 --- a/crates/silkscreen-core/src/service.rs +++ b/crates/silkscreen-core/src/service.rs @@ -3,7 +3,7 @@ //! desktops, MediaCodec on Android), and forwards input events to the host. use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream, UdpSocket}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -49,6 +49,8 @@ pub struct ServiceHandle { struct ServiceShared { stop: AtomicBool, + /// Skip ahead when the picture is this many ms behind; 0 = never. + catch_up_ms: AtomicU32, /// A host is connected (or pairing). busy: AtomicBool, current: Mutex>, @@ -60,6 +62,15 @@ impl ServiceHandle { let _ = self.input.send(ev); } + /// Lets the display skip queued frames (to the next keyframe) whenever + /// the picture falls more than `behind` behind the host, e.g. on a slow + /// Wi-Fi link; `None` shows every frame however late. Takes effect + /// immediately. + pub fn set_catch_up(&self, behind: Option) { + let ms = behind.map_or(0, |d| d.as_millis().clamp(1, u32::MAX as u128) as u32); + self.shared.catch_up_ms.store(ms, Ordering::Relaxed); + } + /// Ends the current session, if any. The host is told the display's /// user ended it, so it waits to be reconnected instead of retrying; the /// display stays available. @@ -119,6 +130,7 @@ pub fn spawn( state.update(|s| s.phase = DisplayPhase::Waiting); let shared = Arc::new(ServiceShared { stop: AtomicBool::new(false), + catch_up_ms: AtomicU32::new(0), busy: AtomicBool::new(false), current: Mutex::new(None), }); @@ -126,10 +138,10 @@ pub fn spawn( let (input_tx, input_rx) = crossbeam_channel::unbounded::(); { - let (sink, state) = (sink.clone(), state.clone()); + let (sink, state, shared) = (sink.clone(), state.clone(), shared.clone()); std::thread::Builder::new() .name("display-video".into()) - .spawn(move || video_thread(udp, video_rx, sink, state))?; + .spawn(move || video_thread(udp, video_rx, sink, state, &shared))?; } { let shared = shared.clone(); @@ -324,21 +336,84 @@ struct Active { last_key_request: Option, } +/// How far behind the host the display is. A frame's arrival time minus +/// its capture time is the one-way delay plus the (unknown, near constant) +/// offset between the two clocks; the smallest such gap seen is the +/// baseline, and anything above it is time the frame spent queued — on the +/// network or in this display's receive buffer. +struct Lag { + origin: Instant, + /// Smallest arrival−capture gap (µs) and when it was last updated. + baseline: Option<(i64, Instant)>, +} + +impl Lag { + fn new() -> Self { + Lag { + origin: Instant::now(), + baseline: None, + } + } + + /// A new stream (its capture times start again). + fn reset(&mut self) { + self.baseline = None; + } + + /// Milliseconds this frame arrived behind the best case. + fn behind_ms(&mut self, arrived: Instant, pts_us: u64) -> f32 { + let gap = arrived.saturating_duration_since(self.origin).as_micros() as i64 - pts_us as i64; + let base = match self.baseline { + // Creep up by 100 ppm to follow the clocks drifting apart; a + // real backlog grows thousands of times faster. + Some((b, at)) => (b + at.elapsed().as_micros() as i64 / 10_000).min(gap), + None => gap, + }; + self.baseline = Some((base, Instant::now())); + (gap - base) as f32 / 1000.0 + } +} + +/// Throws away every datagram already waiting in the socket. +fn drain(udp: &UdpSocket, buf: &mut [u8]) -> usize { + let mut n = 0; + if udp.set_nonblocking(true).is_ok() { + while udp.recv_from(buf).is_ok() { + n += 1; + } + let _ = udp.set_nonblocking(false); + } + n +} + fn video_thread( udp: UdpSocket, ctl: Receiver, sink: Arc, state: Arc>, + shared: &ServiceShared, ) { let mut buf = vec![0u8; 65_536]; let mut active: Option = None; let mut last_stats = Instant::now(); let mut present_total = Duration::ZERO; + let mut lag = Lag::new(); + let mut worst_behind = 0f32; + // Catching up: frames are dropped until the next keyframe. + let mut skipping = false; + let mut last_catch_up: Option = None; + let mut stream_id = None; loop { if !apply_ctl(&ctl, &mut active, &*sink, &state) { return; } + let id = active.as_ref().map(|a| a.reassembler.stream_id()); + if id != stream_id { + stream_id = id; + lag.reset(); + skipping = false; + } let (n, from) = match udp.recv_from(&mut buf) { Ok(r) => r, @@ -374,11 +449,35 @@ fn video_thread( a.request_keyframe(); } if let Some(frame) = res.frame { - match sink.frame(&frame) { - Ok(()) => present_total += frame.first_packet_at.elapsed(), - Err(e) => { - warn!("decode error: {e:#}"); - a.request_keyframe(); + let behind = lag.behind_ms(frame.first_packet_at, frame.pts_us); + worst_behind = worst_behind.max(behind); + let limit = shared.catch_up_ms.load(Ordering::Relaxed) as f32; + if skipping && frame.keyframe { + skipping = false; + } + if !skipping + && limit > 0.0 + && behind > limit + && last_catch_up.is_none_or(|t| t.elapsed() > Duration::from_secs(1)) + { + // Showing these late frames only keeps the picture behind: + // drop everything queued and start again from a keyframe. + let dropped = drain(&udp, &mut buf); + info!( + "{behind:.0} ms behind the host: skipped {dropped} queued packets, waiting for a keyframe" + ); + skipping = true; + last_catch_up = Some(Instant::now()); + a.request_keyframe(); + state.update(|s| s.catch_ups += 1); + } + if !skipping { + match sink.frame(&frame) { + Ok(()) => present_total += frame.first_packet_at.elapsed(), + Err(e) => { + warn!("decode error: {e:#}"); + a.request_keyframe(); + } } } } @@ -395,7 +494,7 @@ fn video_thread( 0 }, })); - debug!("receive stats {s:?}"); + debug!("receive stats {s:?}, {worst_behind:.0} ms behind"); let secs = last_stats.elapsed().as_secs_f32(); state.update(|st| { st.fps = s.frames_delivered as f32 / secs; @@ -406,7 +505,9 @@ fn video_thread( }; st.frames_dropped += s.frames_dropped; st.packets_lost += s.packets_lost; + st.behind_ms = worst_behind; }); + worst_behind = 0.0; a.reassembler.stats = Default::default(); present_total = Duration::ZERO; last_stats = Instant::now(); diff --git a/crates/silkscreen-core/src/status.rs b/crates/silkscreen-core/src/status.rs index 1bf5079..5ffe221 100644 --- a/crates/silkscreen-core/src/status.rs +++ b/crates/silkscreen-core/src/status.rs @@ -79,4 +79,8 @@ pub struct DisplayState { pub latency_ms: f32, pub frames_dropped: u32, pub packets_lost: u32, + /// How far behind the host the picture is (worst in the last second). + pub behind_ms: f32, + /// Times this display skipped ahead to catch up. + pub catch_ups: u32, } diff --git a/crates/silkscreen-platform/src/autostart.rs b/crates/silkscreen-platform/src/autostart.rs new file mode 100644 index 0000000..49c37a7 --- /dev/null +++ b/crates/silkscreen-platform/src/autostart.rs @@ -0,0 +1,172 @@ +//! Starting Silkscreen when the user logs in: the per-user `Run` registry +//! key on Windows, a LaunchAgent on macOS, an XDG autostart entry on Linux. +//! Only the current user is affected, so no administrator rights are needed. + +use anyhow::Result; + +/// The argument the login entry passes, so the app knows how it was started. +pub const LOGIN_ARG: &str = "--autostart"; + +/// Adds (or refreshes, e.g. after the app moved) or removes the login entry. +pub fn set_enabled(enabled: bool) -> Result<()> { + let exe = std::env::current_exe()?.canonicalize()?; + imp::set(enabled, &exe) +} + +#[cfg(windows)] +mod imp { + use std::path::Path; + + use anyhow::{Result, bail}; + use windows::Win32::Foundation::ERROR_FILE_NOT_FOUND; + use windows::Win32::System::Registry::{ + HKEY_CURRENT_USER, REG_SZ, RegDeleteKeyValueW, RegSetKeyValueW, + }; + use windows::core::w; + + const RUN: windows::core::PCWSTR = w!("Software\\Microsoft\\Windows\\CurrentVersion\\Run"); + const VALUE: windows::core::PCWSTR = w!("Silkscreen"); + + pub fn set(enabled: bool, exe: &Path) -> Result<()> { + // canonicalize() gives a \\?\ path; the Run key wants a plain one. + let exe = exe.display().to_string(); + let exe = exe.strip_prefix(r"\\?\").unwrap_or(&exe); + unsafe { + if enabled { + let cmd = format!("\"{exe}\" {}", super::LOGIN_ARG); + let data: Vec = cmd.encode_utf16().chain([0]).collect(); + let r = RegSetKeyValueW( + HKEY_CURRENT_USER, + RUN, + VALUE, + REG_SZ.0, + Some(data.as_ptr().cast()), + (data.len() * 2) as u32, + ); + if r.is_err() { + bail!("cannot write the Run registry key: {r:?}"); + } + } else { + let r = RegDeleteKeyValueW(HKEY_CURRENT_USER, RUN, VALUE); + if r.is_err() && r != ERROR_FILE_NOT_FOUND { + bail!("cannot remove the Run registry value: {r:?}"); + } + } + } + Ok(()) + } +} + +#[cfg(target_os = "macos")] +mod imp { + use std::path::{Path, PathBuf}; + + use anyhow::{Context, Result}; + + const LABEL: &str = "dev.silkscreen"; + + fn plist_path() -> Result { + let home = std::env::var_os("HOME").context("no home directory")?; + Ok(PathBuf::from(home).join(format!("Library/LaunchAgents/{LABEL}.plist"))) + } + + fn escape(s: &str) -> String { + s.replace('&', "&") + .replace('<', "<") + .replace('>', ">") + } + + pub fn set(enabled: bool, exe: &Path) -> Result<()> { + let path = plist_path()?; + if !enabled { + match std::fs::remove_file(&path) { + Err(e) if e.kind() != std::io::ErrorKind::NotFound => return Err(e.into()), + _ => return Ok(()), + } + } + let plist = format!( + r#" + + + + Label + {LABEL} + ProgramArguments + + {} + {} + + RunAtLoad + + ProcessType + Interactive + LimitLoadToSessionType + Aqua + + +"#, + escape(&exe.display().to_string()), + super::LOGIN_ARG + ); + if std::fs::read_to_string(&path).is_ok_and(|c| c == plist) { + return Ok(()); + } + std::fs::create_dir_all(path.parent().unwrap())?; + std::fs::write(&path, plist)?; + Ok(()) + } +} + +#[cfg(target_os = "linux")] +mod imp { + use std::path::{Path, PathBuf}; + + use anyhow::{Context, Result}; + + fn entry_path() -> Result { + let dirs = directories::BaseDirs::new().context("no home directory")?; + Ok(dirs.config_dir().join("autostart/silkscreen.desktop")) + } + + pub fn set(enabled: bool, exe: &Path) -> Result<()> { + let path = entry_path()?; + if !enabled { + match std::fs::remove_file(&path) { + Err(e) if e.kind() != std::io::ErrorKind::NotFound => return Err(e.into()), + _ => return Ok(()), + } + } + // Desktop entry Exec quoting: double quotes, with \ " ` $ escaped. + let mut quoted = String::from('"'); + for c in exe.display().to_string().chars() { + if matches!(c, '\\' | '"' | '`' | '$') { + quoted.push('\\'); + } + quoted.push(c); + } + quoted.push('"'); + let contents = format!( + "[Desktop Entry]\n\ + Type=Application\n\ + Name=Silkscreen\n\ + Comment=Use another computer as a wireless display\n\ + Exec={quoted} {}\n\ + Icon=silkscreen\n\ + X-GNOME-Autostart-enabled=true\n", + super::LOGIN_ARG + ); + if std::fs::read_to_string(&path).is_ok_and(|c| c == contents) { + return Ok(()); + } + std::fs::create_dir_all(path.parent().unwrap())?; + std::fs::write(&path, contents)?; + Ok(()) + } +} + +#[cfg(not(any(windows, target_os = "macos", target_os = "linux")))] +mod imp { + pub fn set(_enabled: bool, _exe: &std::path::Path) -> anyhow::Result<()> { + anyhow::bail!("starting at login isn't supported on this system") + } +} diff --git a/crates/silkscreen-platform/src/lib.rs b/crates/silkscreen-platform/src/lib.rs index 0f9dd64..1495758 100644 --- a/crates/silkscreen-platform/src/lib.rs +++ b/crates/silkscreen-platform/src/lib.rs @@ -12,6 +12,7 @@ use std::time::Duration; use anyhow::Result; use silkscreen_proto::InputEvent; +pub mod autostart; mod keymap; pub mod power; pub mod testpattern; @@ -90,6 +91,12 @@ pub fn open_screen(req: &ScreenRequest) -> Result { if req.kind == SourceKind::TestPattern { return Ok(testpattern::open(req)); } + // Several host sessions may plug in virtual monitors at once. The + // backends find theirs as "the output that just appeared" (and Windows' + // driver reads its mode from one registry slot), so one at a time. + static VIRTUAL: std::sync::Mutex<()> = std::sync::Mutex::new(()); + let _one_at_a_time = (req.kind == SourceKind::Virtual) + .then(|| VIRTUAL.lock().unwrap_or_else(|e| e.into_inner())); #[cfg(target_os = "linux")] return linux::open_screen(req); #[cfg(windows)] @@ -102,6 +109,35 @@ pub fn open_screen(req: &ScreenRequest) -> Result { )) } +/// Tells the user about a fatal error where there may be no terminal to see +/// it (the Windows app has no console): a message box on Windows. +pub fn alert(title: &str, text: &str) { + #[cfg(windows)] + windows::alert(title, text); + #[cfg(not(windows))] + let _ = (title, text); +} + +/// One of this computer's screens, for picking what to mirror. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MonitorInfo { + /// What [`SourceKind::Monitor`] takes to capture this screen. + pub id: String, + pub description: String, + pub main: bool, +} + +/// This computer's screens. Empty where the system asks the user instead +/// (Linux: the screen-sharing dialog). +pub fn monitors() -> Vec { + #[cfg(windows)] + return windows::monitor_list(); + #[cfg(target_os = "macos")] + return macos::monitor_list(); + #[allow(unreachable_code)] + Vec::new() +} + /// Describes which backends are available here, for `silkscreen doctor`. pub fn diagnostics() -> Vec<(String, String)> { #[cfg(target_os = "linux")] diff --git a/crates/silkscreen-platform/src/macos/mod.rs b/crates/silkscreen-platform/src/macos/mod.rs index e5ac015..72f2973 100644 --- a/crates/silkscreen-platform/src/macos/mod.rs +++ b/crates/silkscreen-platform/src/macos/mod.rs @@ -68,6 +68,21 @@ pub fn open_screen(req: &ScreenRequest) -> Result { "existing display" } ); + // ScreenCaptureKit scales to the size asked for, even to another shape. + // An existing screen keeps its own: the largest size that fits. + let fitted; + let req = if guard.is_none() { + let b = CGDisplayBounds(display_id).size; + let scale = (req.width as f64 / b.width).min(req.height as f64 / b.height); + fitted = ScreenRequest { + width: ((b.width * scale).round() as u32).max(2) & !1, + height: ((b.height * scale).round() as u32).max(2) & !1, + ..req.clone() + }; + &fitted + } else { + req + }; let capturer = capture::SckCapturer::start(display_id, req, guard)?; Ok(Screen { capturer: Box::new(capturer), @@ -76,6 +91,18 @@ pub fn open_screen(req: &ScreenRequest) -> Result { }) } +pub fn monitor_list() -> Vec { + let main = CGMainDisplayID(); + active_displays() + .into_iter() + .map(|id| crate::MonitorInfo { + id: id.to_string(), + description: describe(id), + main: id == main, + }) + .collect() +} + pub fn diagnostics() -> Vec<(String, String)> { let mut out = vec![( "virtual displays".into(), diff --git a/crates/silkscreen-platform/src/macos/virtual_display.rs b/crates/silkscreen-platform/src/macos/virtual_display.rs index 46ad469..a2fd654 100644 --- a/crates/silkscreen-platform/src/macos/virtual_display.rs +++ b/crates/silkscreen-platform/src/macos/virtual_display.rs @@ -25,6 +25,17 @@ unsafe extern "C" { fn dispatch_get_global_queue(identifier: isize, flags: usize) -> *mut AnyObject; } +/// A serial number per display name: macOS tells virtual displays apart +/// (and remembers each one's arrangement) by vendor, product and serial, so +/// several at once need different ones, and the same one each time. +fn serial(name: &str) -> u32 { + // FNV-1a; never 0, which macOS treats as "no serial". + let h = name.bytes().fold(0x811c_9dc5u32, |h, b| { + (h ^ b as u32).wrapping_mul(0x0100_0193) + }); + h.max(1) +} + pub struct VirtualDisplay { /// Released on drop, which unplugs the display. _display: Retained, @@ -63,7 +74,7 @@ impl VirtualDisplay { msg_send![&*desc, setSizeInMillimeters: CGSize { width: mm_w, height: mm_h }]; let _: () = msg_send![&*desc, setVendorID: 0x5153u32]; // "SS" let _: () = msg_send![&*desc, setProductID: 0x0001u32]; - let _: () = msg_send![&*desc, setSerialNum: 0x0001u32]; + let _: () = msg_send![&*desc, setSerialNum: serial(&req.name)]; let queue = dispatch_get_global_queue(0, 0); let _: () = msg_send![&*desc, setQueue: queue]; diff --git a/crates/silkscreen-platform/src/windows/mod.rs b/crates/silkscreen-platform/src/windows/mod.rs index 1d9c81d..199e9f1 100644 --- a/crates/silkscreen-platform/src/windows/mod.rs +++ b/crates/silkscreen-platform/src/windows/mod.rs @@ -111,6 +111,31 @@ fn set_mode(device_name: &str, width: u32, height: u32, hz: u32) { } } +pub fn alert(title: &str, text: &str) { + use ::windows::Win32::UI::WindowsAndMessaging::{MB_ICONERROR, MB_OK, MessageBoxW}; + use ::windows::core::HSTRING; + unsafe { + MessageBoxW( + None, + &HSTRING::from(text), + &HSTRING::from(title), + MB_OK | MB_ICONERROR, + ); + } +} + +pub fn monitor_list() -> Vec { + monitors() + .unwrap_or_default() + .iter() + .map(|m| crate::MonitorInfo { + id: m.device_name.clone(), + description: describe(m), + main: (m.rect.left, m.rect.top) == (0, 0), + }) + .collect() +} + fn describe(m: &Monitor) -> String { let r = m.rect; format!( @@ -173,9 +198,16 @@ pub fn open_screen(req: &ScreenRequest) -> Result { let all = monitors()?; let list = all.iter().map(describe).collect::>().join(", "); let chosen = match wanted { - Some(w) => all - .into_iter() - .find(|m| m.device_name.to_lowercase().contains(&w.to_lowercase())), + // Exact first: \\.\DISPLAY1 is also part of \\.\DISPLAY10. + Some(w) => { + let w = w.to_lowercase(); + let exact = all.iter().position(|m| m.device_name.to_lowercase() == w); + let i = exact.or_else(|| { + all.iter() + .position(|m| m.device_name.to_lowercase().contains(&w)) + }); + i.map(|i| all.into_iter().nth(i).unwrap()) + } // Without a name, a lone secondary monitor (dummy plug) is the obvious choice. None => { let secondary: Vec<_> = all diff --git a/crates/silkscreen-proto/src/control.rs b/crates/silkscreen-proto/src/control.rs index bdf28b0..e8d6bf2 100644 --- a/crates/silkscreen-proto/src/control.rs +++ b/crates/silkscreen-proto/src/control.rs @@ -18,6 +18,10 @@ pub enum Codec { H264, Hevc, Av1, + /// PyroWave: an intra-only wavelet codec on the GPU (Vulkan or Metal). + /// Every frame stands alone; needs a few hundred Mbit/s (Ethernet). + /// Experimental. + PyroWave, } impl Codec { @@ -26,6 +30,7 @@ impl Codec { Codec::H264 => "h264", Codec::Hevc => "hevc", Codec::Av1 => "av1", + Codec::PyroWave => "pyrowave", } } } diff --git a/crates/silkscreen-proto/src/lib.rs b/crates/silkscreen-proto/src/lib.rs index 47803c8..1f59b4e 100644 --- a/crates/silkscreen-proto/src/lib.rs +++ b/crates/silkscreen-proto/src/lib.rs @@ -17,7 +17,7 @@ pub use control::*; pub use video::*; /// Bumped whenever the wire format changes incompatibly. -pub const PROTOCOL_VERSION: u16 = 2; +pub const PROTOCOL_VERSION: u16 = 3; /// Default TCP port a display listens on for control connections. pub const DEFAULT_CONTROL_PORT: u16 = 47_810; diff --git a/crates/silkscreen/Cargo.toml b/crates/silkscreen/Cargo.toml index ab7a33b..f4c4564 100644 --- a/crates/silkscreen/Cargo.toml +++ b/crates/silkscreen/Cargo.toml @@ -34,6 +34,18 @@ egui-wgpu = "0.37" egui-winit = "0.37" serde = { version = "1.0.229", features = ["derive"] } postcard = { version = "1.1.3", features = ["use-std"] } +# ksni: the Linux tray talks D-Bus directly, no GTK/libappindicator needed. +tray-icon = { version = "0.26.1", default-features = false, features = ["ksni"] } + +[features] +default = ["pyrowave"] +# The experimental PyroWave codec (see crates/silkscreen-codec/build.rs). +pyrowave = ["silkscreen-codec/pyrowave"] [build-dependencies] embed-resource = "3.0.12" + +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.6.5" +objc2-app-kit = { version = "0.3.2", features = ["NSApplication", "NSImage", "NSRunningApplication"] } +objc2-foundation = { version = "0.3.2", features = ["NSData"] } diff --git a/crates/silkscreen/src/app/mod.rs b/crates/silkscreen/src/app/mod.rs index 6400e72..a732aad 100644 --- a/crates/silkscreen/src/app/mod.rs +++ b/crates/silkscreen/src/app/mod.rs @@ -2,7 +2,9 @@ //! (pair, discover, connect, debug) and, in display mode, the wireless //! monitor itself — video full-screen with egui drawn on top when needed. +mod prefs; mod render; +mod tray; mod ui; use std::collections::BTreeSet; @@ -15,8 +17,8 @@ use anyhow::{Context, Result}; use silkscreen_codec::Picture; use silkscreen_platform::SourceKind; use silkscreen_platform::power::WakeLock; -use silkscreen_proto::{DEFAULT_DATAGRAM_LEN, InputEvent, MouseButton, StreamConfig, TouchPhase}; -use tracing::{info, warn}; +use silkscreen_proto::{InputEvent, MouseButton, StreamConfig, TouchPhase}; +use tracing::{Level, info, warn}; use winit::application::ApplicationHandler; use winit::event::{ElementState, MouseScrollDelta, WindowEvent}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProxy}; @@ -28,24 +30,33 @@ use crate::discovery::Browser; use crate::display::{DecodingSink, DisplayOptions, PictureSink, caps_for}; use crate::host::{HostHandle, HostOptions}; use crate::keymap; +pub use prefs::Prefs; +use prefs::{LoginStart, SourceChoice, StreamPrefs}; use render::{Renderer, UiFrame, VideoUpload}; use silkscreen_core::display::DisplayRole; -use ui::{Action, SourceChoice, UiState, View}; +use silkscreen_core::status::{DisplayPhase, HostPhase}; +use tray::{Tray, TrayCommand, TrayState}; +use ui::{Action, UiState, View}; + +/// Smallest packet size offered: IPv4's minimum datagram size. +pub const MIN_DATAGRAM_LEN: usize = 576; #[derive(Debug, Clone)] pub struct GuiOptions { /// Go straight into display mode (`silkscreen display`). pub start_display: bool, - pub display: DisplayOptions, - /// Display mode in a window instead of fullscreen. - pub windowed: bool, - /// Monitor index for display mode (default: the one the window is on). - pub monitor: Option, - pub vsync: bool, - pub forward_mouse: bool, - pub forward_keyboard: bool, - /// Connect to this display (name or address) as soon as the app opens. - pub connect: Option, + /// Saved settings, with this run's command-line flags applied. + pub prefs: Prefs, + /// `--pairing-code`: a fixed code instead of a random one. + pub fixed_code: Option, + /// Connect to these displays (names or addresses) as soon as the app opens. + pub connect: Vec, + /// Mirror to these displays as soon as the app opens. + pub mirror: Vec, + /// From `-v` / `-vv`. + pub log_level: Level, + /// Started at login (`--autostart`, see `silkscreen_platform::autostart`). + pub autostart: bool, } pub fn run(opts: GuiOptions, ctx: Arc) -> Result<()> { @@ -70,6 +81,7 @@ enum UiEvent { NewFrame, StreamStarted, StreamStopped, + Tray(TrayCommand), } struct WindowSink { @@ -111,6 +123,23 @@ enum Hotkey { Disconnect, Debug, Fullscreen, + HwDecode, +} + +/// Extending this computer to one display. +struct HostSession { + /// What was connected to (name or address), to recognise the display. + target: String, + /// Shows a copy of one of this computer's screens instead of its own + /// virtual monitor. + mirror: bool, + handle: HostHandle, +} + +impl HostSession { + fn active(&self) -> bool { + !self.handle.is_finished() + } } struct Gfx { @@ -127,8 +156,14 @@ struct App { egui: egui::Context, ui: UiState, browser: Option, - host: Option, + /// Host sessions, one per display this computer extends or mirrors to. + hosts: Vec, + /// This computer's screens (for Mirror), refreshed now and then. + screens: Vec, + screens_at: Option, display: Option, + /// The display's decoder, to switch hardware decoding mid-stream. + decoder: Option>, /// A host is streaming to this display. streaming: bool, overlay: bool, @@ -148,6 +183,19 @@ struct App { wake_lock_failed: bool, last_frame: Option, local_ips: Vec, + /// The settings as last saved (or as given), to notice changes. + saved_prefs: Prefs, + /// The stream options the running host was given. + host_stream: StreamPrefs, + /// When stream options last changed, to apply them once they settle. + stream_edited: Option, + /// The level being logged, which the Debug tab can raise. + log_level: Level, + tray: Option, + /// The window is closed to the tray. + hidden: bool, + /// The OS says the window can't be seen (covered, minimised, locked). + occluded: bool, } impl App { @@ -155,15 +203,25 @@ impl App { let egui = egui::Context::default(); ui::style(&egui); App { - ui: UiState::new(ctx.name(), opts.windowed), + ui: UiState::new(ctx.name(), opts.prefs.clone(), opts.log_level), + saved_prefs: opts.prefs.clone(), + host_stream: opts.prefs.stream.clone(), + stream_edited: None, + log_level: opts.log_level, + tray: None, + hidden: false, + occluded: false, ctx, opts, proxy, gfx: None, egui, browser: None, - host: None, + hosts: Vec::new(), + screens: Vec::new(), + screens_at: None, display: None, + decoder: None, streaming: false, overlay: false, hint_until: None, @@ -181,7 +239,12 @@ impl App { } fn init(&mut self, el: &ActiveEventLoop) -> Result<()> { + let app = self.ui.prefs.app.clone(); + let login = self.opts.autostart.then_some(app.login_start); + // Starting in the tray needs the tray icon to get back to the window. + self.hidden = login == Some(LoginStart::Tray) && app.tray; let attrs = Window::default_attributes() + .with_visible(!self.hidden) .with_title("Silkscreen") .with_inner_size(winit::dpi::LogicalSize::new(1180.0, 760.0)) .with_min_inner_size(winit::dpi::LogicalSize::new(640.0, 420.0)) @@ -195,7 +258,7 @@ impl App { "silkscreen", ); let window = Arc::new(el.create_window(attrs)?); - let renderer = Renderer::new(window.clone(), self.opts.vsync)?; + let renderer = Renderer::new(window.clone(), self.ui.prefs.display.vsync)?; let egui = egui_winit::State::new( self.egui.clone(), egui::ViewportId::ROOT, @@ -213,11 +276,23 @@ impl App { Ok(b) => self.browser = Some(b), Err(e) => warn!("LAN discovery unavailable: {e:#}"), } - if self.opts.start_display { + set_app_icon(); + self.set_tray(app.tray); + if self.hidden { + set_dock_icon(false); + } + if app.start_at_login { + // Refresh the login entry, in case the app moved. + set_start_at_login(true); + } + if self.opts.start_display || login == Some(LoginStart::Display) { self.start_display(el)?; } - if let Some(target) = self.opts.connect.clone() { - self.connect(target); + for target in self.opts.connect.clone() { + self.connect(target, false); + } + for target in self.opts.mirror.clone() { + self.connect(target, true); } self.redraw(); Ok(()) @@ -241,20 +316,21 @@ impl App { return Ok(()); } let g = self.gfx.as_ref().context("no window")?; - let monitor = match self.opts.monitor { - Some(i) => el - .available_monitors() - .nth(i) - .context("no monitor with that index")?, - // Wayland only knows a window's monitor once it is shown, and - // has no "primary" monitor; fall back to the first one. - None => g - .window - .current_monitor() - .or_else(|| el.primary_monitor()) - .or_else(|| el.available_monitors().next()) - .context("no monitors")?, - }; + let prefs = &self.ui.prefs.display; + let chosen = prefs.monitor.and_then(|i| { + let m = el.available_monitors().nth(i); + if m.is_none() { + warn!("no monitor {i} (unplugged?); using the window's"); + } + m + }); + // Wayland only knows a window's monitor once it is shown, and has no + // "primary" monitor; fall back to the first one. + let monitor = chosen + .or_else(|| g.window.current_monitor()) + .or_else(|| el.primary_monitor()) + .or_else(|| el.available_monitors().next()) + .context("no monitors")?; let size = monitor.size(); let refresh_mhz = monitor.refresh_rate_millihertz().unwrap_or(60_000); let scale = monitor.scale_factor() as f32; @@ -269,23 +345,24 @@ impl App { video: g.renderer.video.clone(), proxy: Mutex::new(self.proxy.clone()), }); - let caps = caps_for( - &self.ctx, - &self.opts.display, - size.width, - size.height, - refresh_mhz, - scale, - ); - let d = &self.opts.display; + let d = DisplayOptions { + port: prefs.port, + hw_decode: prefs.hw_decode, + fixed_code: self.opts.fixed_code.clone(), + catch_up: catch_up(prefs.skip_behind_ms), + }; + let caps = caps_for(&self.ctx, &d, size.width, size.height, refresh_mhz, scale); + let decoder = DecodingSink::new(sink, d.hw_decode); let role = DisplayRole::start( d.port, d.fixed_code.clone(), self.ctx.clone(), caps, - DecodingSink::new(sink, d.hw_decode), + decoder.clone(), )?; - if !self.ui.display_windowed { + self.decoder = Some(decoder); + role.service.set_catch_up(d.catch_up); + if !prefs.windowed { g.window .set_fullscreen(Some(Fullscreen::Borderless(Some(monitor)))); } @@ -330,9 +407,135 @@ impl App { } } + // -- tray --------------------------------------------------------------- + + fn set_tray(&mut self, on: bool) { + if on == self.tray.is_some() { + return; + } + if !on { + self.tray = None; + if self.hidden { + self.show_window(); + } + return; + } + let proxy = Mutex::new(self.proxy.clone()); + match Tray::new(move |cmd| { + let _ = proxy.lock().unwrap().send_event(UiEvent::Tray(cmd)); + }) { + Ok(t) => { + self.tray = Some(t); + self.update_tray(); + } + Err(e) => warn!("cannot show a tray icon: {e:#}"), + } + } + + /// Closes the window to the tray; connections keep running. + fn hide_window(&mut self) { + self.release_input(); + // In display mode the window *is* the display. + if self.display.is_some() { + self.stop_display(); + } + if let Some(g) = &self.gfx { + g.window.set_visible(false); + } + self.hidden = true; + set_dock_icon(false); + self.update_tray(); + } + + fn show_window(&mut self) { + set_dock_icon(true); + if let Some(g) = &self.gfx { + g.window.set_visible(true); + g.window.focus_window(); + } + self.hidden = false; + self.redraw(); + } + + fn tray_command(&mut self, el: &ActiveEventLoop, cmd: TrayCommand) { + match cmd { + TrayCommand::Open => self.show_window(), + TrayCommand::Disconnect => { + for s in &self.hosts { + s.handle.stop(); + } + if self.streaming { + self.release_input(); + if let Some(d) = &self.display { + d.service.disconnect(); + } + } + } + TrayCommand::ToggleDisplay => { + if self.display.is_some() { + self.stop_display(); + } else { + self.show_window(); + if let Err(e) = self.start_display(el) { + warn!("cannot start display mode: {e:#}"); + } + } + } + TrayCommand::Quit => { + self.release_input(); + el.exit(); + } + } + self.update_tray(); + } + + /// Shows what's happening in the tray menu and tooltip. + fn update_tray(&mut self) { + let hosts: Vec<_> = self.hosts.iter().map(|s| s.handle.state.get()).collect(); + // A pairing code has to be typed into the window. + if self.hidden + && hosts + .iter() + .any(|h| matches!(h.phase, HostPhase::NeedCode(_))) + { + self.show_window(); + } + let Some(tray) = &mut self.tray else { return }; + let display = self.display.as_ref().map(|d| d.service.state.get()); + let names = |want: fn(&HostPhase) -> bool| { + let names: Vec<_> = hosts + .iter() + .filter(|h| want(&h.phase)) + .map(|h| h.display.clone().unwrap_or_else(|| "a display".into())) + .collect(); + (!names.is_empty()).then(|| names.join(", ")) + }; + let status = if let Some(n) = names(|p| matches!(p, HostPhase::Streaming)) { + format!("Extending to {n}") + } else if let Some(n) = names(|p| matches!(p, HostPhase::NeedCode(_))) { + format!("Pairing with {n}") + } else if names(|p| !matches!(p, HostPhase::Stopped(_))).is_some() { + "Connecting…".into() + } else { + match display.as_ref().map(|d| &d.phase) { + Some(DisplayPhase::Streaming { host }) => format!("Showing {host}'s screen"), + Some(DisplayPhase::Pairing { host, .. }) => format!("Pairing with {host}"), + Some(_) => "Display mode: waiting for a host".into(), + None => "Not connected".into(), + } + }; + let hosting = self.hosts.iter().any(HostSession::active); + tray.update(TrayState { + status, + can_disconnect: hosting || self.streaming, + in_display_mode: self.display.is_some(), + }); + } + fn stop_display(&mut self) { self.release_input(); self.display = None; // stops the service and unadvertises + self.decoder = None; self.streaming = false; if let Some(g) = &self.gfx { g.window.set_fullscreen(None); @@ -404,12 +607,13 @@ impl App { KeyCode::KeyG => Some(Hotkey::Disconnect), KeyCode::KeyD => Some(Hotkey::Debug), KeyCode::KeyF => Some(Hotkey::Fullscreen), + KeyCode::KeyH => Some(Hotkey::HwDecode), _ => None, } } fn mouse_suppressed(&self) -> bool { - !self.opts.forward_mouse + !self.ui.prefs.display.forward_mouse || self .last_touch .is_some_and(|t| t.elapsed() < Duration::from_millis(500)) @@ -424,7 +628,7 @@ impl App { let PhysicalKey::Code(code) = event.physical_key else { return true; }; - if !self.opts.forward_keyboard || event.repeat { + if !self.ui.prefs.display.forward_keyboard || event.repeat { return true; // the host's OS does its own key repeat } let Some(evdev) = keymap::evdev(code) else { @@ -504,6 +708,10 @@ impl App { // -- UI ----------------------------------------------------------------- fn draw(&mut self) { + if self.hidden || self.occluded { + return; + } + self.refresh_screens(false); let show_hint = self.hint_until.is_some_and(|t| Instant::now() < t); let ui_frame = if self.ui_visible() { let Some(g) = &mut self.gfx else { return }; @@ -515,10 +723,23 @@ impl App { .as_ref() .map(|b| b.displays()) .unwrap_or_default(), - host: self.host.as_ref().map(|h| h.state.get()), - display: self.display.as_ref().map(|d| d.service.state.get()), + hosts: self + .hosts + .iter() + .map(|s| (s.target.clone(), s.mirror, s.handle.state.get())) + .collect(), + screens: &self.screens, + display: self.display.as_ref().map(|d| { + let mut s = d.service.state.get(); + // The decoder can change mid-stream (switch, fallback). + if let Some(name) = self.decoder.as_ref().and_then(|d| d.decoder_name()) { + s.decoder = Some(name); + } + s + }), local_ips: &self.local_ips, monitor: monitor_desc(&g.window), + monitors: monitor_labels(&g.window), overlay: self.overlay, awake: self.wake_lock.is_some(), }; @@ -550,6 +771,8 @@ impl App { let ppp = out.pixels_per_point; let primitives = self.egui.tessellate(out.shapes, ppp); self.apply(actions); + self.sync_settings(); + self.update_tray(); Some(UiFrame { primitives, textures: out.textures_delta, @@ -558,8 +781,12 @@ impl App { } else { None }; - if let Some(g) = &mut self.gfx { - g.renderer.render(ui_frame); + if let Some(g) = &mut self.gfx + && !g.renderer.render(ui_frame) + { + // Hidden without an Occluded event (some systems never send + // one): look again now and then rather than spinning. + self.repaint_at = Some(Instant::now() + Duration::from_millis(250)); } } @@ -569,18 +796,27 @@ impl App { // Needs the event loop; deferred via a flag picked up in `user_event`. Action::StartDisplay => self.pending_start = true, Action::StopDisplay => self.stop_display(), - Action::Connect(target) => self.connect(target), - Action::SubmitCode(code) => { - if let Some(h) = &self.host { + Action::Connect(target, mirror) => self.connect(target, mirror), + Action::SubmitCode(i, code) => { + if let Some(h) = self.hosts.get(i).map(|s| &s.handle) { h.submit_code(&code); } } - Action::StopHost => { - if let Some(h) = &self.host { + Action::StopHost(i) => { + if let Some(h) = self.hosts.get(i).map(|s| &s.handle) { h.stop(); } } - Action::DismissHost => self.host = None, + Action::StopAllHosts => { + for s in &self.hosts { + s.handle.stop(); + } + } + Action::DismissHost(i) => { + if i < self.hosts.len() { + self.hosts.remove(i); + } + } Action::Forget(key) => self.ctx.paired.remove(&key), Action::Rename(name) => { self.ctx.set_name(&name); @@ -602,30 +838,134 @@ impl App { } } - fn connect(&mut self, target: String) { - if self.host.as_ref().is_some_and(|h| !h.is_finished()) { + /// Saves settings changed in the window and applies those that take + /// effect immediately. + fn sync_settings(&mut self) { + let prefs = &self.ui.prefs; + if *prefs != self.saved_prefs { + let (tray, login) = (prefs.app.tray, prefs.app.start_at_login); + if login != self.saved_prefs.app.start_at_login { + set_start_at_login(login); + } + let (new, old) = (&prefs.display, &self.saved_prefs.display); + if new.vsync != old.vsync + && let Some(g) = &mut self.gfx + { + g.renderer.set_vsync(new.vsync); + } + if new.hw_decode != old.hw_decode + && let Some(d) = &self.decoder + { + d.set_hw_decode(new.hw_decode); + } + if new.skip_behind_ms != old.skip_behind_ms + && let Some(d) = &self.display + { + d.service.set_catch_up(catch_up(new.skip_behind_ms)); + } + if prefs.stream != self.saved_prefs.stream { + self.stream_edited = Some(Instant::now()); + } + prefs.save(); + self.saved_prefs = prefs.clone(); + self.set_tray(tray); + } + self.update_host(); + // Showing debug or trace lines in the log means recording them. + let level = self.ui.log_level.max(Level::INFO); + if level != self.log_level { + crate::set_log_level(level); + self.log_level = level; + } + } + + /// Hands changed stream options to the running host once they have + /// settled, so dragging a slider or typing a size doesn't restart the + /// encoder (or reconnect) on every step. + fn update_host(&mut self) { + const SETTLE: Duration = Duration::from_millis(700); + let Some(edited) = self.stream_edited else { return; + }; + if !self.hosts.iter().any(HostSession::active) || self.ui.prefs.stream == self.host_stream { + self.stream_edited = None; + return; + } + let due = edited + SETTLE; + let now = Instant::now(); + if now < due || self.egui.egui_is_using_pointer() { + let next = due.max(now + Duration::from_millis(100)); + self.repaint_at = Some(self.repaint_at.map_or(next, |t| t.min(next))); + return; + } + for s in self.hosts.iter().filter(|s| s.active()) { + s.handle.update(self.host_options(None, s.mirror)); + } + self.host_stream = self.ui.prefs.stream.clone(); + self.stream_edited = None; + } + + /// Starts extending (or mirroring) to another display, alongside any + /// others. + fn connect(&mut self, target: String, mirror: bool) { + if self.hosts.iter().any(|s| s.target == target && s.active()) { + return; + } + self.hosts.retain(|s| s.target != target); + self.refresh_screens(true); + let opts = self.host_options(Some(target.clone()), mirror); + // Sessions share one set of stream options. + if !self.hosts.iter().any(HostSession::active) { + self.host_stream = self.ui.prefs.stream.clone(); + self.stream_edited = None; + } + self.hosts.push(HostSession { + target, + mirror, + handle: HostHandle::spawn(opts, self.ctx.clone()), + }); + } + + /// Re-reads this computer's screens every few seconds (or now). + fn refresh_screens(&mut self, now: bool) { + if now + || self + .screens_at + .is_none_or(|t| t.elapsed() > Duration::from_secs(2)) + { + self.screens = silkscreen_platform::monitors(); + self.screens_at = Some(Instant::now()); } - let st = &self.ui; - let opts = HostOptions { - target: Some(target), + } + + fn host_options(&self, target: Option, mirror: bool) -> HostOptions { + let st = &self.ui.prefs.stream; + // The chosen screen, else the main one; with no list (Linux) the + // system's screen-sharing dialog asks. + let mirrored = || { + let main = self.screens.iter().find(|m| m.main).map(|m| m.id.clone()); + SourceKind::Monitor(st.mirror_screen.clone().or(main)) + }; + HostOptions { + target, codec: st.codec, bitrate_kbps: (st.bitrate_mbps > 0.0).then_some((st.bitrate_mbps * 1000.0) as u32), - encoder: None, + encoder: st.codec.and(st.encoder.clone()), source: match st.source { + _ if mirror => mirrored(), SourceChoice::Virtual => SourceKind::Virtual, SourceChoice::Monitor => SourceKind::Monitor( Some(st.monitor_name.trim().to_owned()).filter(|s| !s.is_empty()), ), SourceChoice::TestPattern => SourceKind::TestPattern, }, - size: None, - refresh_hz: None, - datagram_len: DEFAULT_DATAGRAM_LEN, - persist: true, + size: st.size, + refresh_hz: st.refresh_hz, + datagram_len: st.datagram_len.max(MIN_DATAGRAM_LEN), + persist: st.reconnect, resolution: st.resolution, - }; - self.host = Some(HostHandle::spawn(opts, self.ctx.clone())); + max_fps: st.max_fps.map(|f| f as f32), + } } } @@ -647,6 +987,31 @@ fn monitor_desc(window: &Window) -> String { ) } +/// Monitors as listed in the display options, numbered in the system's +/// order (macOS names them only "Monitor #"). +fn monitor_labels(window: &Window) -> Vec { + let primary = window.primary_monitor(); + window + .available_monitors() + .enumerate() + .map(|(i, m)| { + let s = m.size(); + format!( + "{}. {} — {}×{}{}", + i + 1, + m.name().unwrap_or_else(|| "Monitor".into()), + s.width, + s.height, + if primary.as_ref() == Some(&m) { + " (main)" + } else { + "" + } + ) + }) + .collect() +} + /// Local addresses worth showing on the waiting screen. fn local_ips() -> Vec { crate::net::local_ip().into_iter().collect() @@ -702,6 +1067,10 @@ impl ApplicationHandler for App { } self.redraw(); } + UiEvent::Tray(cmd) => self.tray_command(el, cmd), + } + if !matches!(ev, UiEvent::NewFrame) { + self.update_tray(); } } @@ -731,8 +1100,12 @@ impl ApplicationHandler for App { fn window_event(&mut self, el: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { match &event { WindowEvent::CloseRequested => { - self.release_input(); - el.exit(); + if self.tray.is_some() { + self.hide_window(); + } else { + self.release_input(); + el.exit(); + } return; } WindowEvent::Resized(size) => { @@ -746,6 +1119,10 @@ impl ApplicationHandler for App { return; } WindowEvent::ModifiersChanged(m) => self.modifiers = m.state(), + WindowEvent::Occluded(o) => { + self.occluded = *o; + self.redraw(); + } _ => {} } @@ -761,6 +1138,15 @@ impl ApplicationHandler for App { Hotkey::Disconnect => self.disconnect_hotkey(), Hotkey::Debug => self.overlay = !self.overlay, Hotkey::Fullscreen => self.toggle_fullscreen(), + Hotkey::HwDecode => { + let d = &mut self.ui.prefs.display; + d.hw_decode = !d.hw_decode; + info!( + "hardware decoding {}", + if d.hw_decode { "on" } else { "off" } + ); + self.sync_settings(); + } } self.redraw(); } @@ -779,6 +1165,64 @@ impl ApplicationHandler for App { } } +fn catch_up(ms: Option) -> Option { + ms.map(|ms| Duration::from_millis(ms as u64)) +} + +fn set_start_at_login(on: bool) { + match silkscreen_platform::autostart::set_enabled(on) { + Ok(()) => info!( + "Silkscreen {} start at login", + if on { "will" } else { "won't" } + ), + Err(e) => warn!("cannot change starting at login: {e:#}"), + } +} + +/// Shows or hides the Dock icon: while the window is closed to the menu +/// bar, Silkscreen is a menu bar app. +#[cfg(target_os = "macos")] +fn set_dock_icon(visible: bool) { + use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy}; + let Some(mtm) = objc2::MainThreadMarker::new() else { + return; + }; + let app = NSApplication::sharedApplication(mtm); + app.setActivationPolicy(if visible { + NSApplicationActivationPolicy::Regular + } else { + NSApplicationActivationPolicy::Accessory + }); + if visible { + set_app_icon(); + #[allow(deprecated)] // `activate` needs macOS 14 + app.activateIgnoringOtherApps(true); + } +} + +/// The Dock and app switcher icon. Silkscreen.app has it in its bundle, but +/// a plain `silkscreen` binary would show the generic executable icon. +#[cfg(target_os = "macos")] +fn set_app_icon() { + use objc2::AllocAnyThread; + use objc2_app_kit::{NSApplication, NSImage}; + use objc2_foundation::NSData; + let Some(mtm) = objc2::MainThreadMarker::new() else { + return; + }; + let png = include_bytes!("../../../../assets/png/macos/icon-512.png"); + let data = NSData::with_bytes(png); + if let Some(image) = NSImage::initWithData(NSImage::alloc(), &data) { + unsafe { NSApplication::sharedApplication(mtm).setApplicationIconImage(Some(&image)) }; + } +} + +#[cfg(not(target_os = "macos"))] +fn set_app_icon() {} + +#[cfg(not(target_os = "macos"))] +fn set_dock_icon(_visible: bool) {} + /// The app icon for the title bar and taskbar (64x64 RGBA from assets/). fn window_icon() -> Option { let rgba = include_bytes!("../../../../assets/icon-64.rgba"); diff --git a/crates/silkscreen/src/app/prefs.rs b/crates/silkscreen/src/app/prefs.rs new file mode 100644 index 0000000..b42a4d8 --- /dev/null +++ b/crates/silkscreen/src/app/prefs.rs @@ -0,0 +1,170 @@ +//! The app's own settings (display and stream options), saved in +//! `gui.json` next to `settings.json` whenever they change in the window. +//! Command-line flags override them for one run. + +use serde::{Deserialize, Serialize}; +use silkscreen_proto::{Codec, DEFAULT_CONTROL_PORT, DEFAULT_DATAGRAM_LEN}; + +use crate::host::Resolution; +use crate::secure::config_dir; + +const FILE: &str = "gui.json"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceChoice { + Virtual, + Monitor, + TestPattern, +} + +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct Prefs { + pub display: DisplayPrefs, + pub stream: StreamPrefs, + pub app: AppPrefs, +} + +/// What the app does when it starts at login. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum LoginStart { + /// Open the window as usual. + #[default] + Window, + /// Stay in the system tray (needs the tray icon). + Tray, + /// Go straight into display mode. + Display, +} + +/// The app itself: tray icon and starting at login (both off by default). +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct AppPrefs { + /// Show a tray icon; closing the window then keeps Silkscreen running. + pub tray: bool, + pub start_at_login: bool, + pub login_start: LoginStart, +} + +/// Display mode (`silkscreen display`). +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct DisplayPrefs { + pub windowed: bool, + /// Index into the system's monitor list; `None` = the one the window is on. + pub monitor: Option, + pub port: u16, + pub hw_decode: bool, + pub vsync: bool, + pub forward_mouse: bool, + pub forward_keyboard: bool, + /// Skip queued frames when the picture falls this many ms behind the + /// host; `None` = show every frame. + pub skip_behind_ms: Option, +} + +impl Default for DisplayPrefs { + fn default() -> Self { + DisplayPrefs { + windowed: false, + monitor: None, + port: DEFAULT_CONTROL_PORT, + hw_decode: true, + vsync: true, + forward_mouse: true, + forward_keyboard: true, + skip_behind_ms: None, + } + } +} + +/// Extending this computer to a display (`silkscreen host`). +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(default)] +pub struct StreamPrefs { + pub source: SourceChoice, + pub monitor_name: String, + /// The screen Mirror sends (an id from `silkscreen_platform::monitors`); + /// `None` = the main screen. + pub mirror_screen: Option, + pub codec: Option, + /// A specific FFmpeg encoder for `codec`; `None` tries the usual ones. + pub encoder: Option, + /// 0 = automatic. + pub bitrate_mbps: f32, + pub resolution: Resolution, + /// Overrides `resolution`. + pub size: Option<(u32, u32)>, + pub refresh_hz: Option, + /// Send at most this many frames a second; `None` = every frame. + pub max_fps: Option, + pub datagram_len: usize, + /// Reconnect when the display goes away (`--once` turns this off). + pub reconnect: bool, +} + +impl Default for StreamPrefs { + fn default() -> Self { + StreamPrefs { + source: SourceChoice::Virtual, + monitor_name: String::new(), + mirror_screen: None, + codec: None, + encoder: None, + bitrate_mbps: 0.0, + resolution: Resolution::Auto, + size: None, + refresh_hz: None, + max_fps: None, + datagram_len: DEFAULT_DATAGRAM_LEN, + reconnect: true, + } + } +} + +impl Prefs { + pub fn load() -> Self { + std::fs::read(config_dir().join(FILE)) + .ok() + .and_then(|b| serde_json::from_slice(&b).ok()) + .unwrap_or_default() + } + + pub fn save(&self) { + let r = std::fs::create_dir_all(config_dir()).and_then(|_| { + std::fs::write( + config_dir().join(FILE), + serde_json::to_vec_pretty(self).unwrap(), + ) + }); + if let Err(e) = r { + tracing::warn!("could not save settings: {e}"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn round_trip() { + let mut p = Prefs::default(); + p.display.monitor = Some(1); + p.stream.codec = Some(Codec::Hevc); + p.stream.size = Some((2560, 1440)); + let json = serde_json::to_string(&p).unwrap(); + assert_eq!(serde_json::from_str::(&json).unwrap(), p); + } + + #[test] + fn missing_fields_keep_defaults() { + let p: Prefs = serde_json::from_str(r#"{"display": {"vsync": false}}"#).unwrap(); + assert!(!p.display.vsync); + assert!(p.display.hw_decode && p.stream.reconnect); + assert_eq!(p.display.port, DEFAULT_CONTROL_PORT); + } +} diff --git a/crates/silkscreen/src/app/render.rs b/crates/silkscreen/src/app/render.rs index f84c88e..6a6b50c 100644 --- a/crates/silkscreen/src/app/render.rs +++ b/crates/silkscreen/src/app/render.rs @@ -234,9 +234,62 @@ impl VideoRect { } } +fn present_mode(modes: &[wgpu::PresentMode], vsync: bool) -> wgpu::PresentMode { + if !vsync && modes.contains(&wgpu::PresentMode::Immediate) { + wgpu::PresentMode::Immediate + } else if modes.contains(&wgpu::PresentMode::Mailbox) { + // New frames replace queued ones: no tearing, no queueing delay. + wgpu::PresentMode::Mailbox + } else { + wgpu::PresentMode::Fifo + } +} + +/// Picks the graphics API and GPU. On Windows that is Direct3D 12 first: +/// some Vulkan drivers (older Intel ones, e.g. HD Graphics 620 on a Surface) +/// crash the process while creating the device. Vulkan is the fallback, and +/// `WGPU_BACKEND=vulkan|dx12|gl` overrides the choice everywhere. +fn adapter( + window: &Arc, +) -> Result<(wgpu::Instance, wgpu::Surface<'static>, wgpu::Adapter)> { + let from_env = wgpu::InstanceDescriptor::new_without_display_handle_from_env(); + let tries = if cfg!(windows) && std::env::var_os("WGPU_BACKEND").is_none() { + vec![wgpu::Backends::DX12, wgpu::Backends::VULKAN] + } else { + vec![from_env.backends] + }; + let mut last = None; + for backends in tries { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends, + ..wgpu::InstanceDescriptor::new_without_display_handle_from_env() + }); + let surface = match instance.create_surface(window.clone()) { + Ok(s) => s, + Err(e) => { + last = Some(anyhow::anyhow!("creating surface ({backends:?}): {e}")); + continue; + } + }; + match pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + ..Default::default() + })) { + Ok(adapter) => return Ok((instance, surface, adapter)), + Err(e) => { + tracing::info!("no {backends:?} GPU: {e}"); + last = Some(anyhow::anyhow!("no GPU adapter ({backends:?}): {e}")); + } + } + } + Err(last.unwrap_or_else(|| anyhow::anyhow!("no GPU adapter"))) +} + pub struct Renderer { surface: wgpu::Surface<'static>, config: wgpu::SurfaceConfiguration, + present_modes: Vec, pipeline: wgpu::RenderPipeline, egui: egui_wgpu::Renderer, pub video: Arc, @@ -252,17 +305,7 @@ pub struct UiFrame { impl Renderer { pub fn new(window: Arc, vsync: bool) -> Result { - let instance = - wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); - let surface = instance - .create_surface(window.clone()) - .context("creating surface")?; - let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - ..Default::default() - })) - .context("no GPU adapter")?; + let (instance, surface, adapter) = adapter(&window)?; tracing::info!( "GPU: {} ({:?})", adapter.get_info().name, @@ -274,6 +317,8 @@ impl Renderer { ..Default::default() })) .context("creating device")?; + tracing::info!("GPU device ready"); + drop(instance); let size = window.inner_size(); let caps = surface.get_capabilities(&adapter); @@ -284,14 +329,7 @@ impl Renderer { .copied() .find(|f| !f.is_srgb()) .unwrap_or(caps.formats[0]); - let present_mode = if !vsync && caps.present_modes.contains(&wgpu::PresentMode::Immediate) { - wgpu::PresentMode::Immediate - } else if caps.present_modes.contains(&wgpu::PresentMode::Mailbox) { - // New frames replace queued ones: no tearing, no queueing delay. - wgpu::PresentMode::Mailbox - } else { - wgpu::PresentMode::Fifo - }; + let present_mode = present_mode(&caps.present_modes, vsync); tracing::info!("swapchain {format:?}, {present_mode:?}"); let mut config = surface .get_default_config(&adapter, size.width.max(1), size.height.max(1)) @@ -362,6 +400,7 @@ impl Renderer { Ok(Renderer { surface, config, + present_modes: caps.present_modes, pipeline, egui, video: Arc::new(VideoUpload { @@ -386,38 +425,61 @@ impl Renderer { self.surface.configure(&self.video.device, &self.config); } + /// Switches between waiting for vertical sync and presenting immediately. + pub fn set_vsync(&mut self, vsync: bool) { + let mode = present_mode(&self.present_modes, vsync); + if mode != self.config.present_mode { + tracing::info!("swapchain {mode:?}"); + self.config.present_mode = mode; + self.surface.configure(&self.video.device, &self.config); + } + } + pub fn max_texture_side(&self) -> usize { self.video.device.limits().max_texture_dimension_2d as usize } - pub fn render(&mut self, ui: Option) { + /// Draws a frame. Returns false if the window is hidden, so the caller + /// can stop redrawing (presenting is what normally paces the redraws). + pub fn render(&mut self, ui: Option) -> bool { self.video.dirty.store(false, Ordering::Release); + let (device, queue) = (&self.video.device, &self.video.queue); + // Upload texture changes even if this frame can't be drawn: egui + // sends each one once (the font atlas only on the first frame), and + // macOS often has no surface texture for a new window's first frame. + if let Some(ui) = &ui { + for (id, deltas) in &ui.textures.set { + for delta in deltas { + self.egui.update_texture(device, queue, *id, delta); + } + } + } let frame = match self.surface.get_current_texture() { wgpu::CurrentSurfaceTexture::Success(f) | wgpu::CurrentSurfaceTexture::Suboptimal(f) => f, wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => { self.surface.configure(&self.video.device, &self.config); - return; + self.free_textures(ui); + return true; + } + // Hidden (covered, minimised, screen locked): nothing to draw + // into, and no refresh to wait for. + _ => { + self.free_textures(ui); + return false; } - _ => return, }; let view = frame.texture.create_view(&Default::default()); let mut enc = self .video .device .create_command_encoder(&Default::default()); - let (device, queue) = (&self.video.device, &self.video.queue); let screen = egui_wgpu::ScreenDescriptor { size_in_pixels: [self.config.width, self.config.height], pixels_per_point: ui.as_ref().map_or(1.0, |u| u.pixels_per_point), }; let mut extra = Vec::new(); if let Some(ui) = &ui { - for (id, deltas) in &ui.textures.set { - for delta in deltas { - self.egui.update_texture(device, queue, *id, delta); - } - } extra = self .egui .update_buffers(device, queue, &mut enc, &ui.primitives, &screen); @@ -462,6 +524,11 @@ impl Renderer { extra.push(enc.finish()); self.video.queue.submit(extra); self.video.queue.present(frame); + self.free_textures(ui); + true + } + + fn free_textures(&mut self, ui: Option) { if let Some(ui) = ui { for id in &ui.textures.free { self.egui.free_texture(id); diff --git a/crates/silkscreen/src/app/tray.rs b/crates/silkscreen/src/app/tray.rs new file mode 100644 index 0000000..089111b --- /dev/null +++ b/crates/silkscreen/src/app/tray.rs @@ -0,0 +1,131 @@ +//! The system tray icon (the menu bar on macOS): shows what Silkscreen is +//! doing and lets the user disconnect, reopen the window or quit while the +//! window is closed. + +use anyhow::Result; +use tray_icon::menu::{Menu, MenuEvent, MenuItem, PredefinedMenuItem}; +use tray_icon::{MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TrayCommand { + Open, + Disconnect, + /// Start display mode, or stop it if running. + ToggleDisplay, + Quit, +} + +/// What the menu shows. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TrayState { + pub status: String, + pub can_disconnect: bool, + pub in_display_mode: bool, +} + +pub struct Tray { + icon: TrayIcon, + status: MenuItem, + disconnect: MenuItem, + display: MenuItem, + shown: Option, +} + +impl Tray { + /// Creates the icon. `send` is called from the platform's tray thread or + /// event loop with each command. + pub fn new(send: impl Fn(TrayCommand) + Send + Sync + 'static) -> Result { + let status = MenuItem::with_id("status", "Silkscreen", false, None); + let open = MenuItem::with_id("open", "Open Silkscreen", true, None); + let disconnect = MenuItem::with_id("disconnect", "Disconnect", false, None); + let display = MenuItem::with_id("display", "Use this computer as a display", true, None); + let quit = MenuItem::with_id("quit", "Quit Silkscreen", true, None); + let menu = Menu::new(); + menu.append_items(&[ + &status, + &PredefinedMenuItem::separator(), + &open, + &disconnect, + &display, + &PredefinedMenuItem::separator(), + &quit, + ])?; + + // macOS: a one-colour template image it tints like the other menu + // bar icons. Elsewhere trays show the colour app icon. + #[cfg(target_os = "macos")] + let builder = TrayIconBuilder::new().with_icon_templated(tray_icon::Icon::from_rgba( + include_bytes!("../../../../assets/tray-macos-36.rgba").to_vec(), + 36, + 36, + )?); + #[cfg(not(target_os = "macos"))] + let builder = TrayIconBuilder::new().with_icon(tray_icon::Icon::from_rgba( + include_bytes!("../../../../assets/icon-64.rgba").to_vec(), + 64, + 64, + )?); + let icon = builder + .with_tooltip("Silkscreen") + .with_menu(Box::new(menu)) + // Windows and Linux: left click opens the window, right click the + // menu. On macOS any click opens the menu, as menu bar items do. + .with_menu_on_left_click(cfg!(target_os = "macos")) + .build()?; + + let send = std::sync::Arc::new(send); + let on_menu = send.clone(); + MenuEvent::set_event_handler(Some(move |e: MenuEvent| { + let cmd = match e.id.0.as_str() { + "open" => TrayCommand::Open, + "disconnect" => TrayCommand::Disconnect, + "display" => TrayCommand::ToggleDisplay, + "quit" => TrayCommand::Quit, + _ => return, + }; + on_menu(cmd); + })); + TrayIconEvent::set_event_handler(Some(move |e: TrayIconEvent| { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = e + && !cfg!(target_os = "macos") + { + send(TrayCommand::Open); + } + })); + Ok(Tray { + icon, + status, + disconnect, + display, + shown: None, + }) + } + + pub fn update(&mut self, state: TrayState) { + if self.shown.as_ref() == Some(&state) { + return; + } + self.status.set_text(&state.status); + self.disconnect.set_enabled(state.can_disconnect); + self.display.set_text(if state.in_display_mode { + "Stop display mode" + } else { + "Use this computer as a display" + }); + let _ = self + .icon + .set_tooltip(Some(format!("Silkscreen — {}", state.status))); + self.shown = Some(state); + } +} + +impl Drop for Tray { + fn drop(&mut self) { + MenuEvent::set_event_handler(None::); + TrayIconEvent::set_event_handler(None::); + } +} diff --git a/crates/silkscreen/src/app/ui.rs b/crates/silkscreen/src/app/ui.rs index 61194aa..d336201 100644 --- a/crates/silkscreen/src/app/ui.rs +++ b/crates/silkscreen/src/app/ui.rs @@ -12,6 +12,8 @@ use egui::{Align, Color32, Layout, RichText, Ui}; use silkscreen_proto::{Codec, PROTOCOL_VERSION}; use tracing::Level; +use super::MIN_DATAGRAM_LEN; +use super::prefs::{Prefs, SourceChoice}; use crate::ctx::{Ctx, KeepAwake}; use crate::discovery::FoundDisplay; use crate::logbuf; @@ -29,45 +31,28 @@ pub enum Tab { Debug, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SourceChoice { - Virtual, - Monitor, - TestPattern, -} - /// Editable UI state that persists between frames. pub struct UiState { pub tab: Tab, pub name_edit: String, pub manual_addr: String, pub code_input: String, - pub source: SourceChoice, - pub monitor_name: String, - pub codec: Option, - /// 0 = automatic. - pub bitrate_mbps: f32, - pub resolution: crate::host::Resolution, - pub display_windowed: bool, + /// Display and stream options; the app saves them when they change. + pub prefs: Prefs, pub log_level: Level, pub log_filter: String, pub diagnostics: Option>, } impl UiState { - pub fn new(name: String, windowed: bool) -> Self { + pub fn new(name: String, prefs: Prefs, log_level: Level) -> Self { UiState { tab: Tab::Home, name_edit: name, manual_addr: String::new(), code_input: String::new(), - source: SourceChoice::Virtual, - monitor_name: String::new(), - codec: None, - bitrate_mbps: 0.0, - resolution: crate::host::Resolution::Auto, - display_windowed: windowed, - log_level: Level::INFO, + prefs, + log_level, log_filter: String::new(), diagnostics: None, } @@ -78,10 +63,13 @@ impl UiState { pub enum Action { StartDisplay, StopDisplay, - Connect(String), - SubmitCode(String), - StopHost, - DismissHost, + /// A display (name or address), and whether to mirror instead of extend. + Connect(String, bool), + /// Index into [`View::hosts`]. + SubmitCode(usize, String), + StopHost(usize), + StopAllHosts, + DismissHost(usize), Forget(String), Rename(String), ToggleOverlay, @@ -92,10 +80,15 @@ pub enum Action { pub struct View<'a> { pub ctx: &'a Ctx, pub displays: Vec, - pub host: Option, + /// Host sessions as `(target, mirroring, state)`, one per display. + pub hosts: Vec<(String, bool, HostState)>, + /// This computer's screens, for Mirror. + pub screens: &'a [silkscreen_platform::MonitorInfo], pub display: Option, pub local_ips: &'a [IpAddr], pub monitor: String, + /// Every monitor of this computer, in the system's order. + pub monitors: Vec, pub overlay: bool, /// The screen is being kept awake right now. pub awake: bool, @@ -134,36 +127,84 @@ fn card(ui: &mut Ui, title: &str, add: impl FnOnce(&mut Ui)) { // --------------------------------------------------------------------------- // Home +/// The app icon as a texture, uploaded once (raw RGBA from assets/: no +/// image decoder needed). +fn icon_texture( + ctx: &egui::Context, + name: &'static str, + rgba: &[u8], + size: usize, +) -> egui::TextureHandle { + let id = egui::Id::unique(name); + if let Some(t) = ctx.data(|d| d.get_temp::(id)) { + return t; + } + let image = egui::ColorImage::from_rgba_unmultiplied([size, size], rgba); + let t = ctx.load_texture(name, image, egui::TextureOptions::LINEAR); + ctx.data_mut(|d| d.insert_temp(id, t.clone())); + t +} + +/// For the header. +fn app_icon(ctx: &egui::Context) -> egui::TextureHandle { + let rgba = include_bytes!("../../../../assets/icon-64.rgba"); + icon_texture(ctx, "app-icon-64", rgba, 64) +} + +/// Side margin shared by the header and the page, so they line up. +const PAGE_MARGIN: i8 = 14; + pub fn home(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec) { - egui::Panel::top("top").show(ui, |ui| { - ui.add_space(6.0); - ui.horizontal(|ui| { - ui.label( - RichText::new("Silkscreen") - .size(22.0) - .strong() - .color(ACCENT), - ); - ui.add_space(16.0); - ui.selectable_value(&mut st.tab, Tab::Home, RichText::new("Home").size(15.0)); - ui.selectable_value(&mut st.tab, Tab::Debug, RichText::new("Debug").size(15.0)); - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { - ui.label( - RichText::new(v.ctx.identity.fingerprint()) - .monospace() - .weak(), - ) - .on_hover_text("This device's key fingerprint — compare it when pairing"); - let r = ui.add(egui::TextEdit::singleline(&mut st.name_edit).desired_width(180.0)); - if r.lost_focus() && st.name_edit.trim() != v.ctx.name() { - actions.push(Action::Rename(st.name_edit.clone())); + // Darker than the page, lighter than text boxes (so they still show). + let header = egui::Frame::new() + .fill(ui.visuals().panel_fill.gamma_multiply(0.75)) + .inner_margin(egui::Margin::symmetric(PAGE_MARGIN, 0)); + egui::Panel::top("top") + .exact_size(54.0) + .frame(header) + .show(ui, |ui| { + // Everything vertically centred in the bar. + ui.horizontal_centered(|ui| { + let icon = app_icon(ui.ctx()); + ui.add(egui::Image::new((icon.id(), egui::vec2(28.0, 28.0)))); + ui.add_space(4.0); + ui.label(RichText::new("Silkscreen").size(19.0).strong()); + ui.add_space(18.0); + for (tab, name) in [(Tab::Home, "Home"), (Tab::Debug, "Debug")] { + let b = egui::Button::selectable(st.tab == tab, RichText::new(name).size(14.0)) + .min_size(egui::vec2(68.0, 28.0)) + .corner_radius(6.0); + if ui.add(b).clicked() { + st.tab = tab; + } } - ui.label("This computer:"); + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + ui.label( + RichText::new(v.ctx.identity.fingerprint()) + .monospace() + .weak(), + ) + .on_hover_text("This device's key fingerprint — compare it when pairing"); + let r = ui.add( + egui::TextEdit::singleline(&mut st.name_edit) + .desired_width(180.0) + .align(egui::Align2::LEFT_CENTER) + .min_size(egui::vec2(0.0, 28.0)), + ); + if r.lost_focus() && st.name_edit.trim() != v.ctx.name() { + actions.push(Action::Rename(st.name_edit.clone())); + } + ui.label(RichText::new("This computer").weak()); + }); }); }); - ui.add_space(4.0); + let page = egui::Frame::central_panel(ui.style()).inner_margin(egui::Margin { + left: PAGE_MARGIN, + right: PAGE_MARGIN, + top: 12, + bottom: 8, }); - egui::CentralPanel::default().show(ui, |ui| { + egui::CentralPanel::default().frame(page).show(ui, |ui| { egui::ScrollArea::vertical() .auto_shrink(false) .show(ui, |ui| match st.tab { @@ -182,10 +223,11 @@ fn home_tab(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec) ); ui.label(RichText::new(&v.monitor).weak()); ui.checkbox( - &mut st.display_windowed, + &mut st.prefs.display.windowed, "Run in a window instead of fullscreen", ); keep_awake_setting(ui, v, actions); + display_options(ui, st, v); ui.add_space(6.0); if ui .add( @@ -211,6 +253,8 @@ fn home_tab(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec) display_card(&mut cols[0], st, actions); cols[0].add_space(8.0); paired_card(&mut cols[0], v, actions); + cols[0].add_space(8.0); + app_card(&mut cols[0], st); extend_card(&mut cols[1], st, v, actions); }); } else { @@ -219,9 +263,53 @@ fn home_tab(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec) extend_card(ui, st, v, actions); ui.add_space(8.0); paired_card(ui, v, actions); + ui.add_space(8.0); + app_card(ui, st); } } +fn app_card(ui: &mut Ui, st: &mut UiState) { + use super::prefs::LoginStart; + let a = &mut st.prefs.app; + let (tray, place) = if cfg!(target_os = "macos") { + ("Show Silkscreen in the menu bar", "menu bar") + } else { + ("Show Silkscreen in the system tray", "tray") + }; + card(ui, "App", |ui| { + ui.checkbox(&mut a.tray, tray).on_hover_text(format!( + "Closing the window then keeps Silkscreen running in the {place}, \ + connections included. Use its icon to reopen the window, disconnect or quit." + )); + ui.checkbox(&mut a.start_at_login, "Start Silkscreen when I log in"); + if a.start_at_login { + let label = |l: LoginStart| match l { + LoginStart::Window => "Open the window".to_owned(), + LoginStart::Tray => format!("Stay in the {place}"), + LoginStart::Display => "Start display mode".to_owned(), + }; + ui.horizontal(|ui| { + ui.label("When it starts:"); + egui::ComboBox::from_id_salt("login-start") + .selected_text(label(a.login_start)) + .show_ui(ui, |ui| { + for l in [LoginStart::Window, LoginStart::Tray, LoginStart::Display] { + ui.selectable_value(&mut a.login_start, l, label(l)); + } + }); + }); + if a.login_start == LoginStart::Tray && !a.tray { + ui.label( + RichText::new(format!( + "Turn on the {place} icon for this; until then the window opens." + )) + .color(WARN), + ); + } + } + }); +} + fn keep_awake_setting(ui: &mut Ui, v: &View, actions: &mut Vec) { let s = v.ctx.settings(); let (mut mode, mut minutes) = (s.keep_awake, s.idle_minutes); @@ -262,16 +350,98 @@ fn keep_awake_setting(ui: &mut Ui, v: &View, actions: &mut Vec) { } } +fn display_options(ui: &mut Ui, st: &mut UiState, v: &View) { + let d = &mut st.prefs.display; + egui::CollapsingHeader::new("Display options").show(ui, |ui| { + egui::Grid::new("display-opts") + .num_columns(2) + .show(ui, |ui| { + if v.monitors.len() > 1 || d.monitor.is_some() { + ui.label("Screen"); + let label = |i: Option| match i { + None => "The one this window is on".to_owned(), + Some(i) => v + .monitors + .get(i) + .cloned() + .unwrap_or_else(|| format!("Monitor {} (not connected)", i + 1)), + }; + egui::ComboBox::from_id_salt("display-monitor") + .selected_text(label(d.monitor)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut d.monitor, None, label(None)); + for i in 0..v.monitors.len() { + ui.selectable_value(&mut d.monitor, Some(i), label(Some(i))); + } + }) + .response + .on_hover_text("Which of this computer's screens becomes the display"); + ui.end_row(); + } + ui.label("Port"); + ui.add(egui::DragValue::new(&mut d.port).range(1024..=65535)) + .on_hover_text(format!( + "TCP port hosts connect to (default {}). Hosts find it automatically; \ + when connecting by address, add it as 192.168.1.20:PORT.", + silkscreen_proto::DEFAULT_CONTROL_PORT + )); + ui.end_row(); + }); + ui.horizontal(|ui| { + ui.label("When the picture falls behind:"); + let label = |ms: Option| match ms { + None => "Show every frame".to_owned(), + Some(ms) => format!("Skip ahead after {ms} ms"), + }; + egui::ComboBox::from_id_salt("skip-behind") + .selected_text(label(d.skip_behind_ms)) + .show_ui(ui, |ui| { + for ms in [None, Some(50), Some(100), Some(150), Some(250), Some(500)] { + ui.selectable_value(&mut d.skip_behind_ms, ms, label(ms)); + } + }) + .response + .on_hover_text( + "On a slow or busy network (Wi-Fi) frames can queue up, and the picture \ + drifts further behind the host. Skipping drops the queued frames and \ + jumps back to live, at the cost of a skipped moment. Try 150 ms on Wi-Fi.", + ); + }); + ui.checkbox(&mut d.hw_decode, "Hardware video decoding") + .on_hover_text("Turn off if the picture is corrupt or black; software always works"); + ui.checkbox(&mut d.vsync, "Wait for the screen's refresh (no tearing)") + .on_hover_text("Off shows each frame the moment it arrives: lowest latency, may tear"); + ui.checkbox( + &mut d.forward_mouse, + "Send this computer's mouse to the host", + ); + ui.checkbox( + &mut d.forward_keyboard, + "Send this computer's keyboard to the host", + ); + ui.label(RichText::new("Port and screen apply the next time display mode starts.").weak()); + }); +} + fn extend_card(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec) { card(ui, "Extend this computer to a display", |ui| { - if let Some(h) = &v.host { - host_status(ui, st, h, actions); + let active = |h: &HostState| !matches!(h.phase, HostPhase::Stopped(_)); + for (i, (_, mirror, h)) in v.hosts.iter().enumerate() { + host_status(ui, st, i, *mirror, h, actions); + ui.separator(); + } + let connected = v.hosts.iter().filter(|(_, _, h)| active(h)).count(); + if connected > 1 { + ui.horizontal(|ui| { + ui.label(format!("Connected to {connected} displays.")); + if ui.button("Disconnect all").clicked() { + actions.push(Action::StopAllHosts); + } + }); ui.separator(); } - let busy = v - .host - .as_ref() - .is_some_and(|h| !matches!(h.phase, HostPhase::Stopped(_))); + let busy = connected > 0; + let in_use = |target: &str| v.hosts.iter().any(|(t, _, h)| t == target && active(h)); ui.label(RichText::new("Displays on this network").strong()); if v.displays.is_empty() { ui.horizontal(|ui| { @@ -285,7 +455,16 @@ fn extend_card(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec "New virtual monitor", - SourceChoice::Monitor => "Existing monitor (dummy plug)", - SourceChoice::TestPattern => "Test pattern", + stream_options(ui, &mut st.prefs.stream, busy, v.screens); + }); +} + +fn stream_options( + ui: &mut Ui, + st: &mut super::prefs::StreamPrefs, + connected: bool, + screens: &[silkscreen_platform::MonitorInfo], +) { + egui::CollapsingHeader::new("Stream options").show(ui, |ui| { + if connected { + ui.label( + RichText::new( + "Changes apply while connected. Source, resolution, custom size and \ + refresh rate reconnect for a second or two.", + ) + .weak(), + ); + } + egui::Grid::new("opts").num_columns(2).show(ui, |ui| { + ui.label("Source"); + egui::ComboBox::from_id_salt("source") + .selected_text(match st.source { + SourceChoice::Virtual => "New virtual monitor", + SourceChoice::Monitor => "Existing monitor (dummy plug)", + SourceChoice::TestPattern => "Test pattern", + }) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut st.source, + SourceChoice::Virtual, + "New virtual monitor", + ); + ui.selectable_value( + &mut st.source, + SourceChoice::Monitor, + "Existing monitor (dummy plug)", + ); + ui.selectable_value(&mut st.source, SourceChoice::TestPattern, "Test pattern"); + }); + ui.end_row(); + if st.source == SourceChoice::Monitor { + ui.label("Monitor"); + ui.add( + egui::TextEdit::singleline(&mut st.monitor_name) + .hint_text("auto (the only secondary)"), + ); + ui.end_row(); + } + ui.label("Screen to mirror"); + if screens.is_empty() { + ui.label(RichText::new("chosen in the screen-sharing dialog").weak()); + } else { + let label = |id: &Option| match id { + None => "Main screen".to_owned(), + Some(id) => screens.iter().find(|m| &m.id == id).map_or_else( + || format!("{id} (not connected)"), + |m| m.description.clone(), + ), + }; + egui::ComboBox::from_id_salt("mirror-screen") + .selected_text(label(&st.mirror_screen)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut st.mirror_screen, None, label(&None)); + for m in screens { + let text = if m.main { + format!("{} (main)", m.description) + } else { + m.description.clone() + }; + ui.selectable_value(&mut st.mirror_screen, Some(m.id.clone()), text); + } }) + .response + .on_hover_text("What Mirror shows on a display"); + } + ui.end_row(); + ui.label("Resolution"); + ui.add_enabled_ui(st.size.is_none(), |ui| { + use crate::host::Resolution as R; + let label = |r: R| match r { + R::Auto => "Automatic (half above 4K)", + R::Full => "Full (sharpest, more latency)", + R::Half => "Half (lowest latency)", + }; + egui::ComboBox::from_id_salt("resolution") + .selected_text(label(st.resolution)) .show_ui(ui, |ui| { - ui.selectable_value( - &mut st.source, - SourceChoice::Virtual, - "New virtual monitor", - ); - ui.selectable_value( - &mut st.source, - SourceChoice::Monitor, - "Existing monitor (dummy plug)", - ); - ui.selectable_value( - &mut st.source, - SourceChoice::TestPattern, - "Test pattern", - ); + for r in [R::Auto, R::Full, R::Half] { + ui.selectable_value(&mut st.resolution, r, label(r)); + } }); - ui.end_row(); - if st.source == SourceChoice::Monitor { - ui.label("Monitor"); - ui.add( - egui::TextEdit::singleline(&mut st.monitor_name) - .hint_text("auto (the only secondary)"), - ); - ui.end_row(); + }); + ui.end_row(); + ui.label("Custom size"); + ui.horizontal(|ui| { + let mut custom = st.size.is_some(); + if ui + .checkbox(&mut custom, "") + .on_hover_text("Use this size instead of the display's own") + .changed() + { + st.size = custom.then_some((1920, 1080)); } - ui.label("Resolution"); + if let Some((w, h)) = &mut st.size { + ui.add(egui::DragValue::new(w).range(320..=8192)); + ui.label("×"); + ui.add(egui::DragValue::new(h).range(240..=8192)); + } + }); + ui.end_row(); + ui.label("Refresh rate"); + ui.horizontal(|ui| { + let mut custom = st.refresh_hz.is_some(); + if ui + .checkbox(&mut custom, "") + .on_hover_text("Use this rate instead of the display's own") + .changed() { - use crate::host::Resolution as R; - let label = |r: R| match r { - R::Auto => "Automatic (half above 4K)", - R::Full => "Full (sharpest, more latency)", - R::Half => "Half (lowest latency)", - }; - egui::ComboBox::from_id_salt("resolution") - .selected_text(label(st.resolution)) - .show_ui(ui, |ui| { - for r in [R::Auto, R::Full, R::Half] { - ui.selectable_value(&mut st.resolution, r, label(r)); - } - }); + st.refresh_hz = custom.then_some(60.0); } - ui.end_row(); - ui.label("Codec"); - egui::ComboBox::from_id_salt("codec") - .selected_text(st.codec.map_or("Automatic", |c| c.name())) - .show_ui(ui, |ui| { - ui.selectable_value(&mut st.codec, None, "Automatic"); - ui.selectable_value(&mut st.codec, Some(Codec::Hevc), "hevc"); - ui.selectable_value(&mut st.codec, Some(Codec::H264), "h264"); - ui.selectable_value(&mut st.codec, Some(Codec::Av1), "av1"); - }); - ui.end_row(); - ui.label("Bitrate"); - ui.horizontal(|ui| { - ui.add( - egui::Slider::new(&mut st.bitrate_mbps, 0.0..=200.0) - .step_by(5.0) - .suffix(" Mbit/s"), - ); - if st.bitrate_mbps == 0.0 { - ui.label(RichText::new("automatic").weak()); + match &mut st.refresh_hz { + Some(hz) => { + ui.add(egui::DragValue::new(hz).range(10.0..=240.0).suffix(" Hz")); + } + None => { + ui.label(RichText::new("the display's").weak()); + } + } + }); + ui.end_row(); + ui.label("Frame rate limit"); + let fps_label = |f: Option| match f { + None => "Off (every frame)".to_owned(), + Some(f) => format!("{f} frames a second"), + }; + egui::ComboBox::from_id_salt("max-fps") + .selected_text(fps_label(st.max_fps)) + .show_ui(ui, |ui| { + for f in [None, Some(60), Some(45), Some(30), Some(20), Some(15)] { + ui.selectable_value(&mut st.max_fps, f, fps_label(f)); + } + }) + .response + .on_hover_text( + "Sends fewer frames (and less data) without changing the screen's own \ + refresh rate. On Wi-Fi, 30 often keeps the picture from falling behind.", + ); + ui.end_row(); + ui.label("Codec"); + let codec_label = |c: Option| match c { + None => "Automatic", + Some(Codec::PyroWave) => "pyrowave (experimental)", + Some(c) => c.name(), + }; + egui::ComboBox::from_id_salt("codec") + .selected_text(codec_label(st.codec)) + .show_ui(ui, |ui| { + for c in [None, Some(Codec::Hevc), Some(Codec::H264), Some(Codec::Av1)] { + ui.selectable_value(&mut st.codec, c, codec_label(c)); } + let pyrowave = !silkscreen_codec::compiled_encoders(Codec::PyroWave).is_empty(); + ui.add_enabled_ui(pyrowave, |ui| { + ui.selectable_value( + &mut st.codec, + Some(Codec::PyroWave), + codec_label(Some(Codec::PyroWave)), + ) + .on_hover_text( + "A GPU wavelet codec: well under a millisecond to encode and decode, \ + but it needs several hundred Mbit/s, so use it on Ethernet. Both \ + computers need a GPU that supports it.", + ) + .on_disabled_hover_text( + "This computer's GPU can't run PyroWave (it needs Vulkan with a recent \ + GPU, or an Apple Silicon Mac).", + ); + }); }); - ui.end_row(); + ui.end_row(); + ui.label("Encoder"); + let encoders = st + .codec + .map(silkscreen_codec::compiled_encoders) + .unwrap_or_default(); + if st + .encoder + .as_ref() + .is_some_and(|e| !encoders.contains(&e.as_str())) + { + st.encoder = None; // belongs to another codec + } + ui.add_enabled_ui(st.codec.is_some(), |ui| { + egui::ComboBox::from_id_salt("encoder") + .selected_text(st.encoder.as_deref().unwrap_or("Automatic")) + .show_ui(ui, |ui| { + ui.selectable_value(&mut st.encoder, None, "Automatic"); + for e in encoders { + ui.selectable_value(&mut st.encoder, Some(e.to_owned()), e); + } + }) + .response + .on_disabled_hover_text("Choose a codec first") + .on_hover_text("Automatic tries hardware encoders first, then software"); }); + ui.end_row(); + ui.label("Bitrate"); + ui.horizontal(|ui| { + ui.add( + egui::Slider::new(&mut st.bitrate_mbps, 0.0..=1000.0) + .logarithmic(true) + .smallest_positive(5.0) + .integer() + .suffix(" Mbit/s"), + ); + if st.bitrate_mbps == 0.0 { + ui.label( + RichText::new(if st.codec == Some(Codec::PyroWave) { + "automatic (about 1 bit per pixel)" + } else { + "automatic" + }) + .weak(), + ); + } + }); + ui.end_row(); + ui.label("Packet size"); + ui.add( + egui::DragValue::new(&mut st.datagram_len) + .range(MIN_DATAGRAM_LEN..=9000) + .suffix(" bytes"), + ) + .on_hover_text(format!( + "UDP datagram size (default {}). Raise to about 8900 on a jumbo-frame network.", + silkscreen_proto::DEFAULT_DATAGRAM_LEN + )); + ui.end_row(); }); + ui.checkbox( + &mut st.reconnect, + "Reconnect automatically if the display goes away", + ); }); } -fn display_row(ui: &mut Ui, d: &FoundDisplay, v: &View, busy: bool, actions: &mut Vec) { +fn display_row( + ui: &mut Ui, + d: &FoundDisplay, + v: &View, + connected: bool, + actions: &mut Vec, +) { let own = d.key == Some(v.ctx.identity.public); let paired = d.key.is_some_and(|k| v.ctx.paired.knows(&k)); let compatible = d.protocol == PROTOCOL_VERSION; @@ -422,10 +774,23 @@ fn display_row(ui: &mut Ui, d: &FoundDisplay, v: &View, busy: bool, actions: &mu ui.with_layout(Layout::right_to_left(Align::Center), |ui| { if !compatible { ui.label(RichText::new("needs update").color(WARN)); + } else if connected { + ui.label(RichText::new("connected").color(GOOD)); } else if !own { - let label = if paired { "Connect" } else { "Pair…" }; - if ui.add_enabled(!busy, egui::Button::new(label)).clicked() { - actions.push(Action::Connect(d.addr.to_string())); + // Right to left: Mirror ends up after Extend. + let pair = (!paired) + .then_some("You'll pair first: type the code this display shows"); + let mirror = ui.button("Mirror").on_hover_text( + pair.unwrap_or("Show a copy of one of this computer's screens"), + ); + if mirror.clicked() { + actions.push(Action::Connect(d.addr.to_string(), true)); + } + let extend = ui + .button("Extend") + .on_hover_text(pair.unwrap_or("Give it its own virtual monitor")); + if extend.clicked() { + actions.push(Action::Connect(d.addr.to_string(), false)); } } }); @@ -433,7 +798,26 @@ fn display_row(ui: &mut Ui, d: &FoundDisplay, v: &View, busy: bool, actions: &mu }); } -fn host_status(ui: &mut Ui, st: &mut UiState, h: &HostState, actions: &mut Vec) { +fn host_status( + ui: &mut Ui, + st: &mut UiState, + i: usize, + mirror: bool, + h: &HostState, + actions: &mut Vec, +) { + // Several sessions show the same widgets; keep their ids apart. + ui.push_id(i, |ui| host_status_inner(ui, st, i, mirror, h, actions)); +} + +fn host_status_inner( + ui: &mut Ui, + st: &mut UiState, + i: usize, + mirror: bool, + h: &HostState, + actions: &mut Vec, +) { let target = h.display.clone().unwrap_or_else(|| "display".into()); match &h.phase { HostPhase::Idle | HostPhase::Connecting(_) => { @@ -441,7 +825,7 @@ fn host_status(ui: &mut Ui, st: &mut UiState, h: &HostState, actions: &mut Vec { ui.horizontal(|ui| { ui.label( - RichText::new(format!("Extending to {target}")) - .color(GOOD) - .strong(), + RichText::new(if mirror { + format!("Mirroring to {target}") + } else { + format!("Extending to {target}") + }) + .color(GOOD) + .strong(), ); ui.with_layout(Layout::right_to_left(Align::Center), |ui| { if ui.button("Disconnect").clicked() { - actions.push(Action::StopHost); + actions.push(Action::StopHost(i)); } }); }); @@ -494,7 +882,7 @@ fn host_status(ui: &mut Ui, st: &mut UiState, h: &HostState, actions: &mut Vec ui.label("Disconnected."), }; if ui.button("OK").clicked() { - actions.push(Action::DismissHost); + actions.push(Action::DismissHost(i)); } }); } @@ -610,10 +998,13 @@ fn debug_tab(ui: &mut Ui, st: &mut UiState, v: &View) { }); }); ui.add_space(8.0); - if let Some(h) = &v.host { - card(ui, "Host session", |ui| { - ui.label(format!("{:?}", h.phase)); - host_stats(ui, h); + for (i, (target, _, h)) in v.hosts.iter().enumerate() { + let title = format!("Host session: {}", h.display.as_deref().unwrap_or(target)); + ui.push_id(i, |ui| { + card(ui, &title, |ui| { + ui.label(format!("{:?}", h.phase)); + host_stats(ui, h); + }); }); ui.add_space(8.0); } @@ -632,7 +1023,7 @@ fn debug_tab(ui: &mut Ui, st: &mut UiState, v: &View) { fn diagnostics() -> Vec<(String, String)> { let mut out = vec![("FFmpeg".to_string(), silkscreen_codec::ffmpeg_version())]; - for codec in [Codec::Hevc, Codec::H264, Codec::Av1] { + for codec in [Codec::Hevc, Codec::H264, Codec::Av1, Codec::PyroWave] { out.push(( format!("{} encoders", codec.name()), silkscreen_codec::compiled_encoders(codec).join(", "), @@ -679,6 +1070,10 @@ fn display_stats(ui: &mut Ui, d: &DisplayState) { "Dropped / lost", format!("{} frames · {} packets", d.frames_dropped, d.packets_lost), ); + row(ui, "Behind host", format!("{:.0} ms (queued)", d.behind_ms)); + if d.catch_ups > 0 { + row(ui, "Skipped ahead", format!("{} times", d.catch_ups)); + } }); } @@ -725,7 +1120,9 @@ fn log_view(ui: &mut Ui, st: &mut UiState, height: f32) { ] { ui.selectable_value(&mut st.log_level, l, l.as_str()); } - }); + }) + .response + .on_hover_text("DEBUG and TRACE also record more detail, like -v and -vv"); ui.add( egui::TextEdit::singleline(&mut st.log_filter) .hint_text("filter") @@ -764,19 +1161,53 @@ fn log_view(ui: &mut Ui, st: &mut UiState, height: f32) { pub fn display_waiting(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Vec) { let d = v.display.clone().unwrap_or_default(); egui::CentralPanel::default() - .frame(egui::Frame::new().fill(Color32::from_rgb(14, 16, 22)).inner_margin(24.0)) + .frame( + egui::Frame::new() + .fill(Color32::from_rgb(14, 16, 22)) + .inner_margin(24.0), + ) .show(ui, |ui| { ui.vertical_centered(|ui| { - ui.add_space((ui.available_height() * 0.18).max(10.0)); - ui.label(RichText::new(v.ctx.name()).size(44.0).strong()); - ui.add_space(12.0); + ui.add_space((ui.available_height() * 0.16).max(10.0)); + // The app's name, then (smaller) this display's name. + ui.label( + RichText::new("Silkscreen") + .size(54.0) + .strong() + .color(ACCENT), + ); + ui.add_space(16.0); + ui.label( + RichText::new(v.ctx.name()) + .size(32.0) + .strong() + .color(Color32::WHITE), + ); + ui.add_space(18.0); match &d.phase { DisplayPhase::Pairing { host, code } => { - ui.label(RichText::new(format!("\"{host}\" wants to use this display")).size(22.0)); - ui.label(RichText::new("Enter this code on that computer:").size(18.0).weak()); + ui.label( + RichText::new(format!("\"{host}\" wants to use this display")) + .size(22.0), + ); + ui.label( + RichText::new("Enter this code on that computer:") + .size(18.0) + .weak(), + ); ui.add_space(8.0); - let spaced = format!("{} {}", &code[..3.min(code.len())], &code[3.min(code.len())..]); - ui.label(RichText::new(spaced).size(96.0).monospace().strong().color(ACCENT)); + let spaced = format!( + "{} {}", + &code[..3.min(code.len())], + &code[3.min(code.len())..] + ); + ui.label( + RichText::new(spaced) + .size(96.0) + .monospace() + .strong() + .color(ACCENT), + ); } _ => { centered_row(ui, "status", |ui| { @@ -784,34 +1215,61 @@ pub fn display_waiting(ui: &mut Ui, st: &mut UiState, v: &View, actions: &mut Ve ui.label(RichText::new("Ready — waiting for a host").size(22.0)); }); ui.label( - RichText::new("On the other computer, open Silkscreen and pick this display.") - .size(16.0) - .weak(), + RichText::new( + "On the other computer, open Silkscreen and pick this display.", + ) + .size(16.0) + .weak(), ); } } ui.add_space(24.0); - let ips = v.local_ips.iter().map(|i| i.to_string()).collect::>().join(" · "); - ui.label(RichText::new(format!("{ips} {}", v.ctx.identity.fingerprint())).monospace().weak()); + let ips = v + .local_ips + .iter() + .map(|i| i.to_string()) + .collect::>() + .join(" · "); + ui.label( + RichText::new(format!("{ips} {}", v.ctx.identity.fingerprint())) + .monospace() + .weak(), + ); ui.add_space(24.0); centered_row(ui, "buttons", |ui| { if ui.button("Stop display mode").clicked() { actions.push(Action::StopDisplay); } - if ui.button(if v.overlay { "Hide debug" } else { "Show debug" }).clicked() { + if ui + .button(if v.overlay { + "Hide debug" + } else { + "Show debug" + }) + .clicked() + { actions.push(Action::ToggleOverlay); } }); - ui.add_space(8.0); - ui.label( - RichText::new(format!( - "{} disconnects / leaves display mode · {} debug overlay · {} fullscreen", - hotkey_label('G'), - hotkey_label('D'), - hotkey_label('F') - )) - .weak(), - ); + ui.add_space(20.0); + // Keyboard shortcuts: one per line, keys on the left. + centered_row(ui, "shortcuts", |ui| { + egui::Grid::new("shortcuts") + .num_columns(2) + .spacing(egui::vec2(18.0, 6.0)) + .show(ui, |ui| { + for (key, what) in [ + ('G', "Disconnect (or leave display mode)"), + ('D', "Show or hide debug info"), + ('F', "Fullscreen on or off"), + ('H', "Hardware decoding on or off"), + ] { + ui.label(RichText::new(hotkey_label(key)).monospace().weak()); + ui.label(RichText::new(what).weak()); + ui.end_row(); + } + }); + }); if v.overlay { ui.add_space(16.0); // A centred block, left-aligned inside (it holds a log). @@ -896,6 +1354,13 @@ pub fn stream_overlay(ctx: &egui::Context, st: &mut UiState, v: &View, show_hint ui.set_max_width(760.0); ui.label(RichText::new("Silkscreen debug").strong().color(ACCENT)); display_stats(ui, &v.display.clone().unwrap_or_default()); + ui.label( + RichText::new(format!( + "{} switches hardware decoding on or off", + hotkey_label('H') + )) + .weak(), + ); log_view(ui, st, 180.0); }); }); diff --git a/crates/silkscreen/src/display/mod.rs b/crates/silkscreen/src/display/mod.rs index 6adcdc1..62491c9 100644 --- a/crates/silkscreen/src/display/mod.rs +++ b/crates/silkscreen/src/display/mod.rs @@ -3,11 +3,11 @@ //! display lives in the GUI ([`crate::app`]); the network service is shared //! with Android in `silkscreen-core`. -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use silkscreen_codec::{Decoder, Picture}; use silkscreen_core::display::DisplayRole; use silkscreen_core::service::FrameSink; @@ -26,7 +26,7 @@ pub trait PictureSink: Send + Sync { /// Decodes with FFmpeg (hardware where possible) into a [`PictureSink`]. pub struct DecodingSink { out: Arc, - hw: bool, + hw: AtomicBool, decoder: Mutex>, } @@ -34,26 +34,53 @@ struct ActiveDecoder { decoder: Decoder, cfg: StreamConfig, errors: u32, + /// The decoder was swapped mid-stream and can only start at a keyframe. + wait_key: bool, } impl DecodingSink { pub fn new(out: Arc, hw: bool) -> Arc { Arc::new(DecodingSink { out, - hw, + hw: AtomicBool::new(hw), decoder: Mutex::new(None), }) } + + /// The decoder in use, if a stream is running. + pub fn decoder_name(&self) -> Option { + let guard = self.decoder.lock().unwrap(); + guard.as_ref().map(|a| a.decoder.name().to_owned()) + } + + /// Switches hardware decoding on or off, mid-stream if one is running. + pub fn set_hw_decode(&self, hw: bool) { + if self.hw.swap(hw, Ordering::Relaxed) == hw { + return; + } + if let Some(a) = self.decoder.lock().unwrap().as_mut() { + match Decoder::open(a.cfg.codec, hw) { + Ok(d) => { + info!("decoding with {}", d.name()); + a.decoder = d; + a.errors = 0; + a.wait_key = true; + } + Err(e) => warn!("cannot switch decoder: {e:#}"), + } + } + } } impl FrameSink for DecodingSink { fn start(&self, cfg: &StreamConfig, host: &str) -> Result { - let decoder = Decoder::open(cfg.codec, self.hw)?; + let decoder = Decoder::open(cfg.codec, self.hw.load(Ordering::Relaxed))?; let name = decoder.name().to_owned(); *self.decoder.lock().unwrap() = Some(ActiveDecoder { decoder, cfg: cfg.clone(), errors: 0, + wait_key: false, }); self.out.stream_started(cfg, host); Ok(name) @@ -62,6 +89,13 @@ impl FrameSink for DecodingSink { fn frame(&self, frame: &EncodedFrame) -> Result<()> { let mut guard = self.decoder.lock().unwrap(); let a = guard.as_mut().context("no stream")?; + if a.wait_key { + if !frame.keyframe { + // The error makes the service ask the host for a keyframe. + bail!("switched decoder; waiting for a keyframe"); + } + a.wait_key = false; + } match a.decoder.decode(&frame.data) { Ok(Some(pic)) => { a.errors = 0; @@ -71,7 +105,7 @@ impl FrameSink for DecodingSink { Ok(None) => Ok(()), Err(e) => { a.errors += 1; - if a.errors >= 3 && self.hw { + if a.errors >= 3 && self.hw.load(Ordering::Relaxed) { // Hardware decoders can reject streams (e.g. beyond their // max resolution); software always works. warn!("falling back to software decoding"); @@ -98,6 +132,8 @@ pub struct DisplayOptions { pub hw_decode: bool, /// Fixed pairing code instead of a random one (tests). pub fixed_code: Option, + /// Skip ahead when this far behind the host (see `set_catch_up`). + pub catch_up: Option, } impl Default for DisplayOptions { @@ -106,6 +142,7 @@ impl Default for DisplayOptions { port: DEFAULT_CONTROL_PORT, hw_decode: true, fixed_code: None, + catch_up: None, } } } @@ -177,6 +214,7 @@ pub fn run_headless(opts: DisplayOptions, ctx: Arc, spec: HeadlessSpec) -> caps, DecodingSink::new(sink.clone(), opts.hw_decode), )?; + role.service.set_catch_up(opts.catch_up); let started = Instant::now(); let mut total = 0u64; loop { diff --git a/crates/silkscreen/src/host.rs b/crates/silkscreen/src/host.rs index 6de6311..230f6d1 100644 --- a/crates/silkscreen/src/host.rs +++ b/crates/silkscreen/src/host.rs @@ -25,7 +25,9 @@ use tracing::{debug, info, warn}; use crate::ctx::Ctx; use crate::discovery; +use crate::mirror; use crate::net::{now_us, tune_tcp, udp_socket}; +use crate::pacer::Pacer; use crate::secure::{self, Refused, SecureWriter}; use crate::status::{HostPhase, HostState, Shared}; @@ -45,10 +47,47 @@ pub struct HostOptions { /// Keep reconnecting after the display goes away. pub persist: bool, pub resolution: Resolution, + /// Send at most this many frames a second (the screen itself keeps its + /// refresh rate): fewer frames for a slow link. + pub max_fps: Option, +} + +impl HostOptions { + /// Changing these means a new virtual monitor or capture: reconnect. + fn screen_differs(&self, o: &HostOptions) -> bool { + (&self.source, self.size, self.resolution, self.refresh_hz) + != (&o.source, o.size, o.resolution, o.refresh_hz) + } + + /// Changing these means a new encoder (and stream) on the same screen. + fn encoder_differs(&self, o: &HostOptions) -> bool { + ( + self.codec, + &self.encoder, + self.bitrate_kbps, + self.datagram_len, + self.max_fps, + ) != ( + o.codec, + &o.encoder, + o.bitrate_kbps, + o.datagram_len, + o.max_fps, + ) + } +} + +/// How a session ended without an error. +enum Ended { + /// The display disconnected. + Disconnected, + /// New settings need a new screen: connect again straight away. + Restart, } /// How many pixels to stream relative to the display's panel. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case")] pub enum Resolution { /// Native up to 4K; half (each axis) above that. Beyond ~8 MP per frame /// capture, encode and decode times add up to noticeable latency. @@ -98,6 +137,7 @@ pub struct HostHandle { stop: Arc, conn: Arc>>, code_tx: crossbeam_channel::Sender, + update: Arc>>, } impl HostHandle { @@ -107,10 +147,12 @@ impl HostHandle { let stop = Arc::new(AtomicBool::new(false)); let conn = Arc::new(Mutex::new(None)); let (code_tx, code_rx) = crossbeam_channel::bounded(1); + let update = Arc::new(Mutex::new(None)); let control = Control { state: state.clone(), stop: stop.clone(), conn: conn.clone(), + update: update.clone(), }; std::thread::Builder::new() .name("host".into()) @@ -121,9 +163,16 @@ impl HostHandle { stop, conn, code_tx, + update, } } + /// Applies new options to the running session (the target stays): + /// encoder settings switch in place, screen settings reconnect. + pub fn update(&self, opts: HostOptions) { + *self.update.lock().unwrap() = Some(opts); + } + /// Answers a pairing request with the code the display shows. pub fn submit_code(&self, code: &str) { let _ = self.code_tx.try_send(code.to_owned()); @@ -154,6 +203,8 @@ pub struct Control { pub state: Arc>, pub stop: Arc, conn: Arc>>, + /// New options from [`HostHandle::update`], not yet applied. + update: Arc>>, } impl Control { @@ -162,9 +213,17 @@ impl Control { state: Shared::new(), stop: Arc::new(AtomicBool::new(false)), conn: Arc::default(), + update: Arc::default(), } } + /// Pending new options, keeping the current target. + fn take_update(&self, current: &HostOptions) -> Option { + let mut new = self.update.lock().unwrap().take()?; + new.target = current.target.clone(); + Some(new) + } + fn stopped(&self) -> bool { self.stop.load(Ordering::Relaxed) } @@ -175,20 +234,27 @@ impl Control { } pub fn run( - opts: HostOptions, + mut opts: HostOptions, ctx: Arc, mut codes: CodeSource, control: Control, ) -> Result<()> { let mut last_err = None; while !control.stopped() { + if let Some(new) = control.take_update(&opts) { + opts = new; + } control.phase(HostPhase::Connecting( opts.target.clone().unwrap_or_else(|| "network".into()), )); let result = resolve_target(opts.target.as_deref()) - .and_then(|addr| run_session(addr, &opts, &ctx, &mut codes, &control)); + .and_then(|addr| run_session(addr, &mut opts, &ctx, &mut codes, &control)); match result { - Ok(()) => info!("display disconnected"), + Ok(Ended::Restart) => { + control.phase(HostPhase::Reconnecting("applying new settings".into())); + continue; + } + Ok(Ended::Disconnected) => info!("display disconnected"), Err(e) => { // Refusals (wrong code, paused, cancelled) need the user, not a retry. if let Some(r) = e.downcast_ref::() { @@ -290,13 +356,18 @@ fn resolve_target(target: Option<&str>) -> Result { /// Codecs to try, in the display's order of preference, that we can encode. fn codec_candidates(caps: &DisplayCaps, forced: Option) -> Result> { if let Some(c) = forced { + if !caps.decoders.contains(&c) { + return Err(Refused(format!("\"{}\" can't decode {}", caps.name, c.name())).into()); + } return Ok(vec![c]); } + // PyroWave is experimental and needs Ethernet-class bandwidth: only + // when picked. let codecs: Vec<_> = caps .decoders .iter() .copied() - .filter(|&c| !compiled_encoders(c).is_empty()) + .filter(|&c| c != Codec::PyroWave && !compiled_encoders(c).is_empty()) .collect(); if codecs.is_empty() { bail!("no codec in common with the display ({:?})", caps.decoders); @@ -306,11 +377,11 @@ fn codec_candidates(caps: &DisplayCaps, forced: Option) -> Result Result<()> { +) -> Result { info!("connecting to {addr}"); let tcp = TcpStream::connect_timeout(&addr, Duration::from_secs(3)).context("connecting")?; tune_tcp(&tcp)?; @@ -359,7 +430,7 @@ fn run_session( caps.decoders ); - let codecs = codec_candidates(&caps, opts.codec)?; + let mut codecs = codec_candidates(&caps, opts.codec)?; let (mode_w, mode_h, scale) = stream_mode(&caps, opts.resolution); if (mode_w, mode_h) != (caps.width, caps.height) && opts.size.is_none() { info!( @@ -373,7 +444,7 @@ fn run_session( .map(|hz| (hz * 1000.0) as u32) .unwrap_or(caps.refresh_mhz); - let screen = open_screen(&ScreenRequest { + let req = ScreenRequest { name: format!("Silkscreen – {}", caps.name), width, height, @@ -382,14 +453,26 @@ fn run_session( physical_size_mm: caps.physical_size_mm, kind: opts.source.clone(), touch: caps.touch, - }) - .context("opening screen source")?; - info!("streaming {}", screen.description); - control - .state - .update(|s| s.source = Some(screen.description.clone())); - let mut capturer = screen.capturer; - let input = screen.input; + }; + // A virtual monitor belongs to this display alone; an existing screen + // (or the test pattern) is captured once and mirrored to every display + // showing it. + let (mirror, mut capturer, input, description) = if opts.source == SourceKind::Virtual { + let screen = open_screen(&req).context("opening screen source")?; + ( + None, + Some(screen.capturer), + screen.input, + screen.description, + ) + } else { + let sub = mirror::join(&req, &caps.decoders, mirror_settings(opts)) + .map_err(|e| Refused(format!("{e:#}")))?; + let (input, description) = (sub.input(), sub.description().to_owned()); + (Some(sub), None, input, description) + }; + info!("streaming {description}"); + control.state.update(|s| s.source = Some(description)); let udp = udp_socket(SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0))?; udp.connect(SocketAddr::new(addr.ip(), caps.video_port))?; @@ -444,69 +527,101 @@ fn run_session( }; let mut stream: Option = None; + let mut pacer = Pacer::new(opts.max_fps); let started = Instant::now(); let mut last_ping = Instant::now(); let mut stats = Throughput::new(); let mut last_keyframe_sent = Instant::now() - Duration::from_secs(1); - let result: Result<()> = (|| { - while !stop.load(Ordering::Relaxed) && !control.stopped() { - // Rate-limit IDRs: on a lossy link back-to-back keyframes only make it worse. - let want_key = keyframe.load(Ordering::Relaxed) - && last_keyframe_sent.elapsed() > Duration::from_millis(100) - && keyframe.swap(false, Ordering::Relaxed); - - let mut err = None; - let got = capturer.next_frame(Duration::from_millis(50), &mut |frame| { - let pts = started.elapsed().as_micros() as u64; - let r = (|| { + let result: Result = if let Some(sub) = &mirror { + mirror_loop( + sub, opts, control, &writer, &udp, &video_key, &stop, &keyframe, &rtt_us, + ) + } else { + let capturer = capturer.as_mut().unwrap(); + (|| { + while !stop.load(Ordering::Relaxed) && !control.stopped() { + if let Some(new) = control.take_update(opts) { + if new.screen_differs(opts) { + info!("new stream settings: reconnecting with a new screen"); + *opts = new; + return Ok(Ended::Restart); + } + if new.encoder_differs(opts) { + info!("new stream settings: restarting the encoder"); + codecs = codec_candidates(&caps, new.codec)?; + stream = None; + pacer.set_max_fps(new.max_fps); + } + *opts = new; + } + + // Rate-limit IDRs: on a lossy link back-to-back keyframes only make it worse. + let want_key = keyframe.load(Ordering::Relaxed) + && last_keyframe_sent.elapsed() > Duration::from_millis(100) + && keyframe.swap(false, Ordering::Relaxed); + + // With a frame rate limit the encoder is set up for that rate. + let enc_refresh = opts + .max_fps + .map_or(refresh_mhz, |f| refresh_mhz.min((f * 1000.0) as u32)); + let mut encode = |frame: &Frame, key: bool| -> Result<()> { + let pts = started.elapsed().as_micros() as u64; let s = ensure_stream( &mut stream, - &frame, + frame, &codecs, - refresh_mhz, + enc_refresh, opts, &writer, &video_key, control, )?; - let key = want_key || s.fresh; + let key = key || s.fresh; s.fresh = false; - s.encode(&frame, pts, key, &udp, &mut stats) - })(); - if let Err(e) = r { - err = Some(e); + s.encode(frame, pts, key, &udp, &mut stats) + }; + let mut err = None; + let got = capturer.next_frame(Duration::from_millis(50), &mut |frame| { + if pacer.admit(&frame, want_key) + && let Err(e) = encode(&frame, want_key) + { + err = Some(e); + } + })?; + if let Some(e) = err { + return Err(e); } - })?; - if let Some(e) = err { - return Err(e); - } - if let Some(s) = stream.as_mut() { - s.poll(started.elapsed().as_micros() as u64, &udp, &mut stats)?; - } - if want_key { - last_keyframe_sent = Instant::now(); - if !got && let Some(s) = stream.as_mut() { - // Static screen: re-send what is already on it as an IDR. - let pts = started.elapsed().as_micros() as u64; - s.encode_again(pts, &udp, &mut stats)?; + if let Some(frame) = pacer.take_due() { + encode(&frame, false)?; + } + if let Some(s) = stream.as_mut() { + s.poll(started.elapsed().as_micros() as u64, &udp, &mut stats)?; + } + if want_key { + last_keyframe_sent = Instant::now(); + if !got && let Some(s) = stream.as_mut() { + // Static screen: re-send what is already on it as an IDR. + let pts = started.elapsed().as_micros() as u64; + s.encode_again(pts, &udp, &mut stats)?; + } } - } - if last_ping.elapsed() >= Duration::from_secs(1) { - last_ping = Instant::now(); - writer.send(&ControlMsg::Ping { t_us: now_us() })?; - if let Some(s) = &stream { - stats.report( - s.encoder.name(), - rtt_us.load(Ordering::Relaxed), - &control.state, - ); + if last_ping.elapsed() >= Duration::from_secs(1) { + last_ping = Instant::now(); + writer.send(&ControlMsg::Ping { t_us: now_us() })?; + if let Some(s) = &stream { + stats.report( + s.encoder.name(), + rtt_us.load(Ordering::Relaxed), + &control.state, + ); + } } } - } - Ok(()) - })(); + Ok(Ended::Disconnected) + })() + }; let _ = writer.send(&ControlMsg::Goodbye); writer.shutdown(); @@ -519,6 +634,113 @@ fn run_session( result } +fn mirror_settings(opts: &HostOptions) -> mirror::Settings { + mirror::Settings { + codec: opts.codec, + encoder: opts.encoder.clone(), + bitrate_kbps: opts.bitrate_kbps, + max_fps: opts.max_fps, + } +} + +/// Sends a shared mirror's stream to this session's display. +#[allow(clippy::too_many_arguments)] +fn mirror_loop( + sub: &mirror::Subscription, + opts: &mut HostOptions, + control: &Control, + writer: &SecureWriter, + udp: &std::net::UdpSocket, + video_key: &VideoKey, + stop: &AtomicBool, + keyframe: &AtomicBool, + rtt_us: &AtomicU64, +) -> Result { + let mut packetizer: Option = None; + let mut stream_id = 0; + let mut encoder = String::new(); + let mut stats = Throughput::new(); + let mut last_ping = Instant::now(); + while !stop.load(Ordering::Relaxed) && !control.stopped() { + if let Some(new) = control.take_update(opts) { + if new.screen_differs(opts) { + info!("new stream settings: reconnecting with a new screen"); + *opts = new; + return Ok(Ended::Restart); + } + if new.encoder_differs(opts) { + info!("new stream settings for the mirror"); + sub.update(mirror_settings(&new)); + if new.datagram_len != opts.datagram_len && packetizer.is_some() { + packetizer = Some(Packetizer::new( + stream_id, + new.datagram_len, + Some(video_key), + )); + } + } + *opts = new; + } + if keyframe.swap(false, Ordering::Relaxed) { + sub.request_keyframe(); + } + match sub.rx.recv_timeout(Duration::from_millis(50)) { + Ok(mirror::Event::Start { + codec, + width, + height, + refresh_mhz, + bitrate_kbps, + encoder: name, + }) => { + stream_id = rand::random::(); + writer.send(&ControlMsg::StartStream(StreamConfig { + codec, + width, + height, + refresh_mhz, + bitrate_kbps, + stream_id, + }))?; + packetizer = Some(Packetizer::new( + stream_id, + opts.datagram_len, + Some(video_key), + )); + let desc = format!( + "{width}x{height} @ {:.2} Hz · {name} · {:.1} Mbit/s (mirrored)", + refresh_mhz as f32 / 1000.0, + bitrate_kbps as f32 / 1000.0 + ); + info!("stream {desc}"); + control.state.update(|s| { + s.stream = Some(desc); + s.phase = HostPhase::Streaming; + }); + encoder = name; + } + Ok(mirror::Event::Frame { data, key, pts }) => { + if let Some(p) = packetizer.as_mut() { + send_frame(p, udp, &data, key, pts)?; + stats.frame(data.len(), key); + } + } + Ok(mirror::Event::Failed(e)) => bail!("{e}"), + Err(RecvTimeoutError::Timeout) => {} + Err(RecvTimeoutError::Disconnected) => bail!("the mirror stopped"), + } + if last_ping.elapsed() >= Duration::from_secs(1) { + last_ping = Instant::now(); + writer.send(&ControlMsg::Ping { t_us: now_us() })?; + if packetizer.is_some() { + stats.set_encode_ms(sub.encode_ms()); + stats.report(&encoder, rtt_us.load(Ordering::Relaxed), &control.state); + } + } + } + Ok(Ended::Disconnected) +} + /// Wraps the input sink to remember held keys and buttons, and releases them /// when the session ends — a dropped connection must not leave Shift (or a /// mouse button) stuck down on the host. @@ -729,6 +951,18 @@ impl ActiveStream { } } +/// ENOBUFS (macOS, Linux), WSAENOBUFS, or a would-block. +fn send_queue_full(e: &std::io::Error) -> bool { + let enobufs = if cfg!(windows) { + 10055 + } else if cfg!(target_os = "macos") { + 55 + } else { + 105 + }; + e.raw_os_error() == Some(enobufs) || e.kind() == std::io::ErrorKind::WouldBlock +} + fn send_frame( p: &mut Packetizer, udp: &std::net::UdpSocket, @@ -740,6 +974,13 @@ fn send_frame( Ok(_) => Ok(()), // The display's socket is momentarily gone (restarting); not fatal. Err(e) if e.kind() == std::io::ErrorKind::ConnectionRefused => Ok(()), + // The link (Wi-Fi) can't keep up and the outgoing queue is full: + // drop the packet like the network would; the display asks for a + // keyframe. Ending the session would be worse. + Err(e) if send_queue_full(&e) => { + debug!("send queue full, dropped a packet"); + Ok(()) + } Err(e) => Err(e), })?; Ok(()) @@ -775,6 +1016,11 @@ impl Throughput { self.encode_total += d; } + /// For a mirror, which encodes elsewhere: the average per frame. + fn set_encode_ms(&mut self, ms: f32) { + self.encode_total = Duration::from_secs_f64(ms as f64 / 1000.0 * self.frames as f64); + } + fn report(&mut self, encoder: &str, rtt_us: u64, state: &Shared) { let secs = self.since.elapsed().as_secs_f64(); let enc_ms = if self.frames > 0 { diff --git a/crates/silkscreen/src/main.rs b/crates/silkscreen/src/main.rs index 0fddf22..d0addda 100644 --- a/crates/silkscreen/src/main.rs +++ b/crates/silkscreen/src/main.rs @@ -9,18 +9,22 @@ mod app; mod display; mod host; mod keymap; +mod mirror; +mod pacer; use silkscreen_core::{ctx, discovery, logbuf, net, secure, status}; +use std::sync::OnceLock; use std::time::Duration; use anyhow::{Result, bail}; use clap::{Parser, Subcommand, ValueEnum}; use silkscreen_platform::SourceKind; use silkscreen_proto::{Codec, DEFAULT_CONTROL_PORT, DEFAULT_DATAGRAM_LEN, PROTOCOL_VERSION}; +use tracing::Level; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; -use tracing_subscriber::{EnvFilter, Layer}; +use tracing_subscriber::{EnvFilter, Layer, reload}; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -32,9 +36,17 @@ struct Cli { #[arg(long, global = true)] name: Option, /// Open the app and extend this computer to the given display (name or - /// address) right away — handy for a desktop shortcut. + /// address) right away — handy for a desktop shortcut. Repeat it to + /// extend to several displays. #[arg(long)] - connect: Option, + connect: Vec, + /// Like --connect, but the display shows a copy of this computer's main + /// screen (or the one picked in Stream options). Repeatable. + #[arg(long)] + mirror: Vec, + /// Started at login: open as the app's login setting says. + #[arg(long, hide = true)] + autostart: bool, #[command(subcommand)] command: Option, } @@ -44,8 +56,9 @@ enum Command { /// Open the app straight in display mode: this computer becomes a /// wireless display that hosts can extend onto. Display { - #[arg(long, default_value_t = DEFAULT_CONTROL_PORT)] - port: u16, + /// Port to listen on (default: the app's setting, normally 47810). + #[arg(long)] + port: Option, /// Run in a window instead of fullscreen. #[arg(long)] windowed: bool, @@ -64,6 +77,10 @@ enum Command { /// Don't forward this computer's keyboard to the host. #[arg(long)] no_keyboard: bool, + /// Skip queued frames to catch up when the picture is more than this + /// many milliseconds behind the host (e.g. 150 on Wi-Fi). + #[arg(long, value_name = "MS")] + skip_behind: Option, /// Keep this screen from sleeping while it shows a host (saved). #[arg(long, value_enum)] keep_awake: Option, @@ -117,6 +134,10 @@ enum Command { /// UDP datagram size; raise to ~8900 on a jumbo-frame network. #[arg(long, default_value_t = DEFAULT_DATAGRAM_LEN)] mtu: usize, + /// Send at most this many frames a second (e.g. 30 on Wi-Fi); the + /// screen keeps its refresh rate. + #[arg(long, value_name = "FPS")] + max_fps: Option, /// Exit when the display disconnects instead of reconnecting. #[arg(long)] once: bool, @@ -137,6 +158,10 @@ enum Command { }, /// Show codecs, hardware acceleration and capture backends available here. Doctor, + /// Try PyroWave on this GPU (run by the app in a separate process, so a + /// driver crash can't take the app down). Exit code 0 if it works. + #[command(hide = true)] + PyrowaveProbe, } #[derive(Clone, Copy, ValueEnum)] @@ -161,6 +186,8 @@ enum CodecArg { H264, Hevc, Av1, + /// Experimental GPU wavelet codec; needs a few hundred Mbit/s. + Pyrowave, } #[derive(Clone, Copy, PartialEq, Eq, ValueEnum)] @@ -173,6 +200,22 @@ enum SourceArg { TestPattern, } +static SET_LOG_LEVEL: OnceLock> = OnceLock::new(); + +/// Changes how much is logged at runtime: the GUI's `-v` / `-vv`. +pub fn set_log_level(level: Level) { + if let Some(set) = SET_LOG_LEVEL.get() { + set(level); + } +} + +fn log_filter(level: Level) -> EnvFilter { + let l = level.as_str().to_lowercase(); + EnvFilter::new(format!( + "warn,silkscreen={l},silkscreen_codec={l},silkscreen_platform={l}" + )) +} + fn parse_size(s: &str) -> Result<(u32, u32)> { let Some((w, h)) = s.split_once(['x', 'X']) else { bail!("expected WIDTHxHEIGHT, got {s:?}") @@ -180,6 +223,71 @@ fn parse_size(s: &str) -> Result<(u32, u32)> { Ok((w.trim().parse()?, h.trim().parse()?)) } +/// Opens this run's log file, keeping the previous run's as `.old.log`. +fn open_log(path: &std::path::Path) -> Option { + let _ = std::fs::create_dir_all(path.parent()?); + let _ = std::fs::rename(path, path.with_extension("old.log")); + std::fs::File::create(path).ok() +} + +/// Runs `silkscreen pyrowave-probe` and reports whether PyroWave worked +/// there (a crash or a hang counts as no). +#[cfg(feature = "pyrowave")] +fn probe_pyrowave_in_child() -> bool { + use std::io::Read; + use std::process::{Command, Stdio}; + use std::time::Instant; + let Ok(exe) = std::env::current_exe() else { + return false; + }; + let mut child = match Command::new(exe) + .arg("pyrowave-probe") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(c) => c, + Err(e) => { + tracing::warn!("cannot run the PyroWave check: {e}"); + return false; + } + }; + let mut stderr = child.stderr.take(); + let output = std::thread::spawn(move || { + let mut s = String::new(); + if let Some(e) = stderr.as_mut() { + let _ = e.read_to_string(&mut s); + } + s + }); + let deadline = Instant::now() + Duration::from_secs(20); + let status = loop { + match child.try_wait() { + Ok(Some(status)) => break Some(status), + Ok(None) if Instant::now() < deadline => std::thread::sleep(Duration::from_millis(50)), + _ => { + let _ = child.kill(); + break None; + } + } + }; + let ok = status.is_some_and(|s| s.success()); + if !ok { + let out = output.join().unwrap_or_default(); + let tail: Vec<_> = out.lines().rev().take(15).collect(); + tracing::warn!( + "PyroWave check {} on this GPU:\n{}", + match status { + Some(s) => format!("failed ({s})"), + None => "hung".to_owned(), + }, + tail.into_iter().rev().collect::>().join("\n") + ); + } + ok +} + #[cfg(windows)] fn attach_console() { unsafe extern "system" { @@ -196,56 +304,87 @@ fn main() -> Result<()> { } let cli = Cli::parse(); let level = match cli.verbose { - 0 => "info", - 1 => "debug", - _ => "trace", - }; - let filter = || { - EnvFilter::try_from_default_env().unwrap_or_else(|_| { - EnvFilter::new(format!( - "warn,silkscreen={level},silkscreen_codec={level},silkscreen_platform={level}" - )) - }) + 0 => Level::INFO, + 1 => Level::DEBUG, + _ => Level::TRACE, }; + let filter = || EnvFilter::try_from_default_env().unwrap_or_else(|_| log_filter(level)); + let (term_filter, term_reload) = reload::Layer::new(filter()); + let (buf_filter, buf_reload) = reload::Layer::new(filter()); + let (file_filter, file_reload) = reload::Layer::new(filter()); + // The app (not one-shot commands) also logs to a file: on Windows it + // has no console, so that is where a crash can be looked into. + let gui_run = matches!( + cli.command, + None | Some(Command::Display { headless: None, .. }) + ); + let log_path = secure::config_dir().join("silkscreen.log"); + let log_file = gui_run.then(|| open_log(&log_path)).flatten(); tracing_subscriber::registry() .with( tracing_subscriber::fmt::layer() .with_target(false) - .with_filter(filter()), + .with_filter(term_filter), ) - .with(logbuf::BufferLayer.with_filter(filter())) + .with(logbuf::BufferLayer.with_filter(buf_filter)) + .with(log_file.map(|f| { + tracing_subscriber::fmt::layer() + .with_target(false) + .with_ansi(false) + .with_writer(std::sync::Mutex::new(f)) + .with_filter(file_filter) + })) .init(); + let _ = SET_LOG_LEVEL.set(Box::new(move |l| { + let _ = term_reload.reload(log_filter(l)); + let _ = buf_reload.reload(log_filter(l)); + let _ = file_reload.reload(log_filter(l)); + })); + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + let bt = std::backtrace::Backtrace::force_capture(); + tracing::error!("crashed: {info}\n{bt}"); + default_hook(info); + if gui_run { + silkscreen_platform::alert( + "Silkscreen stopped working", + &format!("{info}\n\nDetails are in {}", log_path.display()), + ); + } + })); + tracing::info!( + "silkscreen {} ({} {})", + env!("CARGO_PKG_VERSION"), + std::env::consts::OS, + std::env::consts::ARCH + ); + + #[cfg(feature = "pyrowave")] + if !matches!(cli.command, Some(Command::PyrowaveProbe)) { + silkscreen_codec::pyrowave::set_probe(probe_pyrowave_in_child); + } let ctx = ctx::Ctx::load()?; if let Some(name) = &cli.name { ctx.override_name(name); } - let gui = |start_display, display, windowed, monitor, vsync, mouse, keyboard| { + let gui = |start_display, prefs, fixed_code| { app::run( app::GuiOptions { start_display, - display, - windowed, - monitor, - vsync, - forward_mouse: mouse, - forward_keyboard: keyboard, + prefs, + fixed_code, connect: cli.connect.clone(), + mirror: cli.mirror.clone(), + log_level: level, + autostart: cli.autostart, }, ctx.clone(), ) }; let Some(command) = cli.command else { - return gui( - false, - display::DisplayOptions::default(), - false, - None, - true, - true, - true, - ); + return gui(false, app::Prefs::load(), None); }; match command { Command::Display { @@ -256,6 +395,7 @@ fn main() -> Result<()> { no_vsync, no_mouse, no_keyboard, + skip_behind, keep_awake, idle_minutes, pairing_code, @@ -290,22 +430,31 @@ fn main() -> Result<()> { } }); } - let opts = display::DisplayOptions { - port, - hw_decode: !no_hw_decode, - fixed_code: pairing_code, - }; match headless { - Some(spec) => display::run_headless(opts, ctx.clone(), spec), - None => gui( - true, - opts, - windowed, - monitor, - !no_vsync, - !no_mouse, - !no_keyboard, + Some(spec) => display::run_headless( + display::DisplayOptions { + port: port.unwrap_or(DEFAULT_CONTROL_PORT), + hw_decode: !no_hw_decode, + fixed_code: pairing_code, + catch_up: skip_behind.map(|ms| Duration::from_millis(ms as u64)), + }, + ctx.clone(), + spec, ), + None => { + // Flags override the app's saved settings for this run. + let mut prefs = app::Prefs::load(); + let d = &mut prefs.display; + d.port = port.unwrap_or(d.port); + d.windowed |= windowed; + d.monitor = monitor.or(d.monitor); + d.hw_decode &= !no_hw_decode; + d.vsync &= !no_vsync; + d.forward_mouse &= !no_mouse; + d.forward_keyboard &= !no_keyboard; + d.skip_behind_ms = skip_behind.or(d.skip_behind_ms); + gui(true, prefs, pairing_code) + } } } Command::Host { @@ -319,6 +468,7 @@ fn main() -> Result<()> { refresh, mtu, once, + max_fps, code, resolution, } => host::run( @@ -328,6 +478,7 @@ fn main() -> Result<()> { CodecArg::H264 => Codec::H264, CodecArg::Hevc => Codec::Hevc, CodecArg::Av1 => Codec::Av1, + CodecArg::Pyrowave => Codec::PyroWave, }), bitrate_kbps: bitrate.map(|m| (m * 1000.0) as u32), encoder, @@ -340,6 +491,7 @@ fn main() -> Result<()> { refresh_hz: refresh, datagram_len: mtu, persist: !once, + max_fps, resolution: match resolution { ResolutionArg::Auto => host::Resolution::Auto, ResolutionArg::Full => host::Resolution::Full, @@ -413,6 +565,13 @@ fn main() -> Result<()> { } Ok(()) } + Command::PyrowaveProbe => { + #[cfg(feature = "pyrowave")] + let ok = silkscreen_codec::pyrowave::probe_here(); + #[cfg(not(feature = "pyrowave"))] + let ok = false; + std::process::exit(if ok { 0 } else { 1 }); + } Command::Doctor => { println!("silkscreen {}", env!("CARGO_PKG_VERSION")); println!( @@ -421,9 +580,9 @@ fn main() -> Result<()> { ctx.identity.fingerprint() ); println!("FFmpeg {}", silkscreen_codec::ffmpeg_version()); - for codec in [Codec::H264, Codec::Hevc, Codec::Av1] { + for codec in [Codec::H264, Codec::Hevc, Codec::Av1, Codec::PyroWave] { println!( - " {:<5} encoders: {}", + " {:<8} encoders: {}", codec.name(), silkscreen_codec::compiled_encoders(codec).join(", ") ); diff --git a/crates/silkscreen/src/mirror.rs b/crates/silkscreen/src/mirror.rs new file mode 100644 index 0000000..7be40ba --- /dev/null +++ b/crates/silkscreen/src/mirror.rs @@ -0,0 +1,457 @@ +//! Mirroring: one capture and one encode of a screen, shared by every host +//! session showing that screen. Each session only packetizes, encrypts (with +//! its own display's key) and sends. +//! +//! Sessions whose source is an existing monitor or the test pattern join the +//! mirror for that source, starting it if it isn't running; the mirror stops +//! once its last session leaves. Size and refresh come from the session that +//! started it (other displays scale the picture to fit); the codec is one +//! every joined display can decode. + +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use std::sync::{Arc, Mutex, Weak}; +use std::time::{Duration, Instant}; + +use anyhow::{Context, Result, bail}; +use crossbeam_channel::{Receiver, Sender}; +use silkscreen_codec::{ + Encoder, EncoderConfig, RgbFrame, RgbLayout, compiled_encoders, default_bitrate_kbps, +}; +use silkscreen_platform::{Frame, InputSink, PixelLayout, ScreenRequest, SourceKind, open_screen}; +use silkscreen_proto::{Codec, InputEvent}; +use tracing::{info, warn}; + +use crate::pacer::Pacer; + +/// What a session sends its display. +#[derive(Clone)] +pub enum Event { + /// A new stream begins (first, and whenever the encoder is reopened). + Start { + codec: Codec, + width: u32, + height: u32, + refresh_mhz: u32, + bitrate_kbps: u32, + encoder: String, + }, + Frame { + data: Arc<[u8]>, + key: bool, + pts: u64, + }, + /// The mirror cannot continue (capture or every encoder failed). + Failed(String), +} + +/// Encoder settings, from the sessions' stream options. +#[derive(Debug, Clone, PartialEq, Default)] +pub struct Settings { + pub codec: Option, + pub encoder: Option, + pub bitrate_kbps: Option, + /// Frame rate limit for the shared stream. + pub max_fps: Option, +} + +struct Sub { + id: u64, + tx: Sender, + decoders: Vec, +} + +struct State { + subs: Vec, + next_id: u64, + settings: Settings, + /// Bumped when the settings or the set of displays change. + generation: u64, + /// The stream being sent, for sessions that join mid-way. + current: Option, + /// Set by the capture thread when it has stopped for good. + closed: bool, +} + +struct Shared { + kind: SourceKind, + description: String, + state: Mutex, + keyframe: AtomicBool, + /// Average encode time in µs, for the sessions' stats. + encode_us: AtomicU32, + input: Mutex>, +} + +/// Running mirrors, by source. +static MIRRORS: Mutex>> = Mutex::new(Vec::new()); + +/// A session's membership of a mirror; leaves it when dropped. +pub struct Subscription { + shared: Arc, + id: u64, + pub rx: Receiver, +} + +/// Joins the mirror of `req.kind`, starting it (with `req`'s size) if +/// needed. `decoders` are what this session's display can decode. +pub fn join(req: &ScreenRequest, decoders: &[Codec], settings: Settings) -> Result { + if let Some(c) = settings.codec + && !decoders.contains(&c) + { + bail!("this display can't decode {}", c.name()); + } + let mut mirrors = MIRRORS.lock().unwrap(); + mirrors.retain(|w| w.strong_count() > 0); + let running = mirrors + .iter() + .filter_map(Weak::upgrade) + .find(|m| m.kind == req.kind && !m.state.lock().unwrap().closed); + let shared = match running { + Some(m) => m, + None => { + let shared = start(req, settings.clone())?; + mirrors.push(Arc::downgrade(&shared)); + shared + } + }; + drop(mirrors); + + let (tx, rx) = crossbeam_channel::unbounded(); + let mut st = shared.state.lock().unwrap(); + let id = st.next_id; + st.next_id += 1; + // The newest session's options win, as they do for live changes. + if st.settings != settings { + st.settings = settings; + } + st.generation += 1; + if let Some(cur) = &st.current { + let _ = tx.send(cur.clone()); + } + st.subs.push(Sub { + id, + tx, + decoders: decoders.to_vec(), + }); + let n = st.subs.len(); + drop(st); + shared.keyframe.store(true, Ordering::Relaxed); + if n > 1 { + info!("mirroring {} to {n} displays", shared.description); + } + Ok(Subscription { shared, id, rx }) +} + +impl Subscription { + pub fn description(&self) -> &str { + &self.shared.description + } + + pub fn request_keyframe(&self) { + self.shared.keyframe.store(true, Ordering::Relaxed); + } + + /// Changes the encoder settings for every display of this mirror. + pub fn update(&self, settings: Settings) { + let mut st = self.shared.state.lock().unwrap(); + if st.settings != settings { + st.settings = settings; + st.generation += 1; + } + } + + pub fn encode_ms(&self) -> f32 { + self.shared.encode_us.load(Ordering::Relaxed) as f32 / 1000.0 + } + + /// Input from this display goes to the mirrored screen. + pub fn input(&self) -> Box { + Box::new(MirrorInput(self.shared.clone())) + } +} + +impl Drop for Subscription { + fn drop(&mut self) { + let mut st = self.shared.state.lock().unwrap(); + st.subs.retain(|s| s.id != self.id); + st.generation += 1; + } +} + +struct MirrorInput(Arc); + +impl InputSink for MirrorInput { + fn inject(&mut self, ev: &InputEvent) -> Result<()> { + self.0.input.lock().unwrap().inject(ev) + } +} + +fn start(req: &ScreenRequest, settings: Settings) -> Result> { + let screen = open_screen(req).context("opening screen source")?; + info!("mirroring {}", screen.description); + let shared = Arc::new(Shared { + kind: req.kind.clone(), + description: screen.description, + state: Mutex::new(State { + subs: Vec::new(), + next_id: 0, + settings, + generation: 0, + current: None, + closed: false, + }), + keyframe: AtomicBool::new(true), + encode_us: AtomicU32::new(0), + input: Mutex::new(screen.input), + }); + let refresh_mhz = req.refresh_mhz; + let s = shared.clone(); + let mut capturer = screen.capturer; + std::thread::Builder::new() + .name("mirror".into()) + .spawn(move || { + if let Err(e) = run(&s, capturer.as_mut(), refresh_mhz) { + warn!("mirror stopped: {e:#}"); + broadcast(&s, &Event::Failed(format!("{e:#}"))); + } + let mut st = s.state.lock().unwrap(); + st.closed = true; + st.current = None; + })?; + Ok(shared) +} + +fn broadcast(shared: &Shared, ev: &Event) { + let st = shared.state.lock().unwrap(); + for sub in &st.subs { + // A session that has fallen behind skips frames; it asks for a + // keyframe once its display notices the gap. + if matches!(ev, Event::Frame { .. }) && sub.tx.len() > 8 { + continue; + } + let _ = sub.tx.send(ev.clone()); + } +} + +/// Codecs every display of the mirror can decode, best first. +fn common_codecs(st: &State) -> Vec { + let candidates = match st.settings.codec { + Some(c) => vec![c], + None => vec![Codec::Hevc, Codec::H264, Codec::Av1], + }; + candidates + .into_iter() + .filter(|c| st.subs.iter().all(|s| s.decoders.contains(c))) + .filter(|&c| !compiled_encoders(c).is_empty()) + .collect() +} + +struct Active { + encoder: Encoder, + codec: Codec, + settings: Settings, + width: u32, + height: u32, + generation: u64, +} + +/// The capture thread: capture → encode → every session, until the last +/// session has been gone for a moment. +fn run( + shared: &Shared, + capturer: &mut dyn silkscreen_platform::Capturer, + refresh_mhz: u32, +) -> Result<()> { + let started = Instant::now(); + let mut active: Option = None; + let mut empty_since: Option = None; + let mut last_key = Instant::now() - Duration::from_secs(1); + let mut encode_total = Duration::ZERO; + let mut encoded = 0u32; + let mut pacer = Pacer::new(None); + loop { + let (generation, max_fps) = { + let mut st = shared.state.lock().unwrap(); + if st.subs.is_empty() { + let since = *empty_since.get_or_insert_with(Instant::now); + // A session reconnecting (or restarting for new settings) + // rejoins within this time and keeps the capture. + if since.elapsed() > Duration::from_secs(3) { + st.closed = true; + info!("stopped mirroring {}", shared.description); + return Ok(()); + } + } else { + empty_since = None; + } + (st.generation, st.settings.max_fps) + }; + pacer.set_max_fps(max_fps); + // With a frame rate limit the encoder is set up for that rate. + let enc_refresh = max_fps.map_or(refresh_mhz, |f| refresh_mhz.min((f * 1000.0) as u32)); + let want_key = last_key.elapsed() > Duration::from_millis(100) + && shared.keyframe.swap(false, Ordering::Relaxed); + let mut encode = |frame: &Frame, key: bool| -> Result<()> { + let pts = started.elapsed().as_micros() as u64; + let a = ensure_encoder(shared, &mut active, frame, generation, enc_refresh)?; + let rgb = rgb_frame(frame, a.width, a.height); + let t = Instant::now(); + a.encoder.encode(&rgb, pts, key, |data, key| { + broadcast( + shared, + &Event::Frame { + data: data.into(), + key, + pts, + }, + ); + })?; + encode_total += t.elapsed(); + encoded += 1; + Ok(()) + }; + let mut err = None; + let got = capturer.next_frame(Duration::from_millis(50), &mut |frame| { + if empty_since.is_none() + && pacer.admit(&frame, want_key) + && let Err(e) = encode(&frame, want_key) + { + err = Some(e); + } + })?; + if let Some(e) = err { + return Err(e); + } + if empty_since.is_none() + && let Some(frame) = pacer.take_due() + { + encode(&frame, false)?; + } + if let Some(a) = active.as_mut() { + let pts = started.elapsed().as_micros() as u64; + a.encoder.poll(|data, key| { + broadcast( + shared, + &Event::Frame { + data: data.into(), + key, + pts, + }, + ); + })?; + if want_key && !got { + // Static screen: re-send what is on it as a keyframe. + a.encoder.encode_again(pts, true, |data, key| { + broadcast( + shared, + &Event::Frame { + data: data.into(), + key, + pts, + }, + ); + })?; + } + } + if want_key { + last_key = Instant::now(); + } + if encoded >= 15 { + let us = encode_total.as_micros() as u32 / encoded; + shared.encode_us.store(us, Ordering::Relaxed); + (encode_total, encoded) = (Duration::ZERO, 0); + } + } +} + +fn rgb_frame<'a>(frame: &Frame<'a>, width: u32, height: u32) -> RgbFrame<'a> { + RgbFrame { + width, + height, + stride: frame.stride, + layout: match frame.layout { + PixelLayout::Bgrx => RgbLayout::Bgrx, + PixelLayout::Rgbx => RgbLayout::Rgbx, + }, + data: frame.data, + } +} + +/// (Re)opens the encoder when the captured size, the settings or the set of +/// displays changed, and tells every session about the new stream. +fn ensure_encoder<'a>( + shared: &Shared, + active: &'a mut Option, + frame: &Frame, + generation: u64, + refresh_mhz: u32, +) -> Result<&'a mut Active> { + let (w, h) = (frame.width & !1, frame.height & !1); + if let Some(a) = active + && (a.width, a.height) == (w, h) + && a.generation == generation + { + return Ok(active.as_mut().unwrap()); + } + let (codecs, settings) = { + let st = shared.state.lock().unwrap(); + (common_codecs(&st), st.settings.clone()) + }; + // A display joined or left, or settings were re-sent unchanged: keep + // the encoder if every display can still decode it. + if let Some(a) = active + && (a.width, a.height) == (w, h) + && a.settings == settings + && codecs.contains(&a.codec) + { + a.generation = generation; + return Ok(active.as_mut().unwrap()); + } + if codecs.is_empty() { + bail!("no codec that every mirrored display can decode"); + } + let mut errors = Vec::new(); + for codec in codecs { + let bitrate_kbps = settings + .bitrate_kbps + .unwrap_or_else(|| default_bitrate_kbps(codec, w, h, refresh_mhz)); + let cfg = EncoderConfig { + codec, + width: w, + height: h, + refresh_mhz, + bitrate_kbps, + encoder: settings.encoder.clone(), + }; + match Encoder::open(&cfg) { + Ok(encoder) => { + let start = Event::Start { + codec, + width: w, + height: h, + refresh_mhz, + bitrate_kbps, + encoder: encoder.name().to_owned(), + }; + info!( + "mirror stream {w}x{h} · {} · {:.1} Mbit/s", + encoder.name(), + bitrate_kbps as f32 / 1000.0 + ); + shared.state.lock().unwrap().current = Some(start.clone()); + broadcast(shared, &start); + shared.keyframe.store(true, Ordering::Relaxed); + return Ok(active.insert(Active { + encoder, + codec, + settings, + width: w, + height: h, + generation, + })); + } + Err(e) => errors.push(format!("{e:#}")), + } + } + bail!("cannot encode {w}x{h}:\n{}", errors.join("\n")) +} diff --git a/crates/silkscreen/src/pacer.rs b/crates/silkscreen/src/pacer.rs new file mode 100644 index 0000000..c6bb27d --- /dev/null +++ b/crates/silkscreen/src/pacer.rs @@ -0,0 +1,86 @@ +//! The stream's frame rate limit: lets at most one frame through per +//! interval, so a slow link (Wi-Fi) gets fewer, not later, frames. A frame +//! that arrives too soon is kept (copied) and sent when its turn comes, so +//! the last change on screen always reaches the display. + +use std::time::{Duration, Instant}; + +use silkscreen_platform::{Frame, PixelLayout}; + +pub struct Pacer { + interval: Option, + /// When the next frame may go. Advanced by whole intervals, so a 30 fps + /// limit on a 75 Hz screen averages 30 (gaps of 2 and 3 frames) rather + /// than settling on every third frame (25). + next: Option, + held: Vec, + held_frame: Option<(u32, u32, usize, PixelLayout)>, +} + +impl Pacer { + pub fn new(max_fps: Option) -> Self { + let mut p = Pacer { + interval: None, + next: None, + held: Vec::new(), + held_frame: None, + }; + p.set_max_fps(max_fps); + p + } + + pub fn set_max_fps(&mut self, max_fps: Option) { + self.interval = max_fps + .filter(|f| *f > 0.0) + .map(|f| Duration::from_secs_f32(1.0 / f)); + if self.interval.is_none() { + self.held_frame = None; + self.next = None; + } + } + + fn due(&self) -> bool { + // A little early is fine: capture times jitter around the rate. + self.next + .is_none_or(|t| Instant::now() + Duration::from_millis(2) >= t) + } + + fn sent(&mut self) { + if let Some(iv) = self.interval { + let now = Instant::now(); + // After a quiet spell, start counting from now (no burst). + let next = self.next.map_or(now, |t| t.max(now - iv)) + iv; + self.next = Some(next); + } + } + + /// For a newly captured frame: true to encode it now (`force`: a + /// keyframe is wanted), false if it was held back for later. + pub fn admit(&mut self, frame: &Frame, force: bool) -> bool { + if force || self.due() { + self.sent(); + self.held_frame = None; + return true; + } + self.held.clear(); + self.held.extend_from_slice(frame.data); + self.held_frame = Some((frame.width, frame.height, frame.stride, frame.layout)); + false + } + + /// A held-back frame whose turn has come. + pub fn take_due(&mut self) -> Option> { + if self.held_frame.is_none() || !self.due() { + return None; + } + let (width, height, stride, layout) = self.held_frame.take()?; + self.sent(); + Some(Frame { + width, + height, + stride, + layout, + data: &self.held, + }) + } +} diff --git a/scripts/fetch-submodules.sh b/scripts/fetch-submodules.sh new file mode 100755 index 0000000..6c79f47 --- /dev/null +++ b/scripts/fetch-submodules.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Fetches PyroWave and the parts of Granite it builds with (volk and the +# Vulkan headers, not the rest of Granite's own submodules). +set -euo pipefail +cd "$(dirname "$0")/.." +git submodule update --init --depth 1 third_party/pyrowave third_party/granite +git -C third_party/granite submodule update --init --depth 1 \ + third_party/volk third_party/khronos/vulkan-headers diff --git a/scripts/package-linux.sh b/scripts/package-linux.sh index ec7c47d..34327b3 100755 --- a/scripts/package-linux.sh +++ b/scripts/package-linux.sh @@ -31,7 +31,7 @@ ffmpeg_url="https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpe -w /src "$image" bash -euo pipefail -c ' apt-get update -qq apt-get install -y -qq --no-install-recommends \ - ca-certificates curl xz-utils build-essential clang libclang-dev pkg-config \ + ca-certificates curl xz-utils build-essential clang libclang-dev pkg-config cmake \ libpipewire-0.3-dev >/dev/null [ -x /cargo/bin/cargo ] || curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal >/dev/null export PATH=/cargo/bin:$PATH @@ -52,7 +52,12 @@ ffmpeg_url="https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpe strip $out/silkscreen # Only what the binary loads: avcodec (+ swresample) and avutil. for lib in avcodec avutil swresample; do cp -a $ff/lib/lib$lib.so* $out/lib/; done - cp /src/README.md $out/ + # PyroWave (experimental codec), unless built with --no-default-features. + for lib in $target/release/libpyrowave-shared.so*; do + [ -f "$lib" ] && cp -a "$lib" $out/lib/ && cp /src/third_party/pyrowave/LICENSE $out/PYROWAVE-LICENSE.txt + done + cp /src/README.md /src/LICENSE-MIT /src/LICENSE-APACHE /src/THIRD-PARTY-NOTICES.md \ + /src/THIRD-PARTY-LICENSES.md $out/ [ -f $ff/LICENSE.txt ] && cp $ff/LICENSE.txt $out/FFMPEG-LICENSE.txt cd /src/dist && tar czf silkscreen-linux-$ARCH.tar.gz silkscreen-linux-$ARCH echo "built dist/silkscreen-linux-$ARCH.tar.gz" diff --git a/scripts/package-windows.sh b/scripts/package-windows.sh index 29373ee..8fc3e18 100755 --- a/scripts/package-windows.sh +++ b/scripts/package-windows.sh @@ -2,7 +2,7 @@ # Cross-compile silkscreen.exe from Linux and bundle it with FFmpeg's DLLs. # # Needs: rustup target x86_64-pc-windows-gnu, mingw64-gcc + mingw64-headers, -# clang, and a Windows FFmpeg 8 *shared* build (e.g. BtbN's +# clang, CMake (for PyroWave), and a Windows FFmpeg 8 *shared* build (e.g. BtbN's # ffmpeg-n8.1-latest-win64-lgpl-shared-8.1.zip) unpacked at $FFMPEG_DIR. # # FFMPEG_DIR=~/ffmpeg-win64 scripts/package-windows.sh @@ -23,9 +23,15 @@ out=dist/silkscreen-windows-x64 rm -rf "$out" && mkdir -p "$out" cp target/x86_64-pc-windows-gnu/release/silkscreen.exe "$out/" x86_64-w64-mingw32-strip "$out/silkscreen.exe" +# PyroWave (experimental codec), unless built with --no-default-features. +for dll in target/x86_64-pc-windows-gnu/release/*pyrowave*.dll; do + [ -f "$dll" ] || continue + cp "$dll" "$out/" && x86_64-w64-mingw32-strip "$out/$(basename "$dll")" + cp third_party/pyrowave/LICENSE "$out/PYROWAVE-LICENSE.txt" +done # Only the libraries silkscreen.exe actually loads (avcodec needs swresample). for lib in avcodec avutil swresample; do cp "$FFMPEG_DIR"/bin/$lib-*.dll "$out/"; done -cp README.md "$out/" +cp README.md LICENSE-MIT LICENSE-APACHE THIRD-PARTY-NOTICES.md THIRD-PARTY-LICENSES.md "$out/" [ -f "$FFMPEG_DIR/LICENSE.txt" ] && cp "$FFMPEG_DIR/LICENSE.txt" "$out/FFMPEG-LICENSE.txt" (cd dist && rm -f silkscreen-windows-x64.zip && zip -qr silkscreen-windows-x64.zip silkscreen-windows-x64) echo "built dist/silkscreen-windows-x64.zip" diff --git a/scripts/render-icons.sh b/scripts/render-icons.sh index db7807f..d42f88f 100755 --- a/scripts/render-icons.sh +++ b/scripts/render-icons.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -# Renders every platform icon from assets/icon.svg (needs ImageMagick). +# Renders every platform icon from assets/icon.svg, plus the macOS ones from +# icon-macos.svg and tray-macos.svg (needs ImageMagick and librsvg). set -euo pipefail cd "$(dirname "$0")/../assets" for s in 16 24 32 48 64 128 256 512 1024; do @@ -9,3 +10,12 @@ done magick png/icon-{16,24,32,48,64,128,256}.png icon.ico # Window icon at runtime: raw RGBA, so the app needs no image decoder. magick png/icon-64.png -depth 8 rgba:icon-64.rgba +# macOS: the icon on Apple's grid (Dock, Silkscreen.app), and the menu bar +# glyph as raw RGBA (a template image: macOS tints it). +mkdir -p png/macos +for s in 16 32 64 128 256 512 1024; do + rsvg-convert -w $s -h $s icon-macos.svg -o png/macos/icon-$s.png +done +rsvg-convert -w 36 -h 36 tray-macos.svg -o /tmp/silkscreen-tray-36.png +magick /tmp/silkscreen-tray-36.png -depth 8 rgba:tray-macos-36.rgba +rm /tmp/silkscreen-tray-36.png diff --git a/third_party/granite b/third_party/granite new file mode 160000 index 0000000..5309042 --- /dev/null +++ b/third_party/granite @@ -0,0 +1 @@ +Subproject commit 53090429539bf3404250d18fe58db971f0907c04 diff --git a/third_party/pyrowave b/third_party/pyrowave new file mode 160000 index 0000000..21f099d --- /dev/null +++ b/third_party/pyrowave @@ -0,0 +1 @@ +Subproject commit 21f099d24df0ab0e4c10c3288c924b6b1717e581 From 0bd81b3a9184e838f20a3eb50483ae776595b4d3 Mon Sep 17 00:00:00 2001 From: BenTech2 <5576214+BenTech2@users.noreply.github.com> Date: Wed, 21 Oct 2026 13:28:02 -0400 Subject: [PATCH 2/3] License files, third-party credits, and why Silkscreen exists - LICENSE-MIT and LICENSE-APACHE (the project was already MIT OR Apache-2.0 but had no license texts). - THIRD-PARTY-NOTICES.md: FFmpeg (LGPL v3 in the Windows and Linux downloads, GPL v3 Homebrew build in the macOS app), PyroWave, Granite, volk, Vulkan-Headers, AndroidX/Compose, the Parsec VDD interface, fonts. - THIRD-PARTY-LICENSES.md: every Rust crate's license, generated by cargo-about (scripts/third-party-licenses.sh); all of these ship in the downloads. - README: Silkscreen exists to keep displays without video inputs (the original Surface Studio, Retina iMacs) out of e-waste, and will always be free and open source, with no paid features. Co-Authored-By: Claude Opus 5.5 --- LICENSE-APACHE | 202 + LICENSE-MIT | 21 + README.md | 88 +- THIRD-PARTY-LICENSES.md | 8507 +++++++++++++++++++++++++++++++ THIRD-PARTY-NOTICES.md | 91 + about.hbs | 28 + about.toml | 30 + scripts/third-party-licenses.sh | 7 + 8 files changed, 8966 insertions(+), 8 deletions(-) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 THIRD-PARTY-LICENSES.md create mode 100644 THIRD-PARTY-NOTICES.md create mode 100644 about.hbs create mode 100644 about.toml create mode 100755 scripts/third-party-licenses.sh diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..f3bbcf1 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 BenTech2 and the Silkscreen contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c213ee8..c2c33ff 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,31 @@ native panel (resolution, refresh rate, and — on KDE/macOS — scale). The OS treats it like any other monitor: arrange it, drag windows to it, set it as primary. When the session ends the monitor is unplugged again. +## Why Silkscreen + +Some of the best screens ever made can only show what their own computer +gives them. The original **Surface Studio**'s 28-inch 4500×3000 touch and pen +panel, a **Retina iMac**'s 5K display: neither has a video input, so when the +computer inside gets old or slow, the whole thing tends to become e-waste — +display and all. Silkscreen exists to keep screens like these useful: run it +on the old machine and it becomes a wireless monitor for a newer one, at its +native resolution and with its touch screen still working. + +Silkscreen is and will always be: + +- **Free** — no paid tiers, no "pro" features, no subscriptions, no ads. + Everything it can do is in the free version, because there is only one + version. +- **Open source** — all of it, under the MIT or Apache 2.0 license (your + choice). Use it, change it, share it. + ## Download Grab the file for your system from the [Releases](https://github.com/BenTech2/silkscreen/releases) page: | System | File | Install | |---|---|---| -| Windows 10/11 (x64) | `silkscreen-windows-x64.zip` | Unzip anywhere, run `silkscreen.exe`. Allow it on *Private* networks when the firewall asks. | +| Windows 10/11 (x64) | `silkscreen-windows-x64.zip` | Unzip anywhere, run `silkscreen.exe`. The app isn't code-signed yet, so Windows may say *Windows protected your PC*: choose **More info → Run anyway**. Allow it on *Private* networks when the firewall asks. | | macOS 12.3+ (Apple Silicon) | `silkscreen-macos-arm64.zip` | Unzip, then run `xattr -dr com.apple.quarantine silkscreen-macos-arm64` once (the build isn't notarized) and open `Silkscreen.app`. From a terminal: `Silkscreen.app/Contents/MacOS/silkscreen`. | | Linux x86_64 | `silkscreen-linux-x86_64.tar.gz` | `tar xf` it and run `./silkscreen`. Bundles its own FFmpeg; needs glibc 2.36+ (Debian 12, Ubuntu 22.10, Fedora 37 or newer) and PipeWire. | | Raspberry Pi 4 / 5 (64-bit Raspberry Pi OS) | `silkscreen-linux-aarch64.tar.gz` | Same as Linux. Best as a **display**: the Pi 4 decodes H.264 in hardware, the Pi 5 in software (fine at 1080p60). | @@ -53,10 +71,44 @@ and pen all control the host**. Hotkeys on the display (never sent to the host): | **Ctrl+Alt+Shift+G** | **Ctrl+Option+Shift+G** or **Cmd+Option+Shift+G** | disconnect (the host shows *disconnected from the display* and waits; click **Connect** to resume). Outside a session it leaves display mode | | Ctrl+Alt+Shift+D | Ctrl/Cmd+Option+Shift+D | debug overlay: live stats and log | | Ctrl+Alt+Shift+F | Ctrl/Cmd+Option+Shift+F | toggle fullscreen | +| Ctrl+Alt+Shift+H | Ctrl/Cmd+Option+Shift+H | hardware decoding on/off, mid-stream | The **Debug** tab shows the same log the terminal does, plus codecs, hardware acceleration, capture backends and live stats for both sides. +**Several displays at once.** Each display in the list has two buttons: +**Extend** gives it its own virtual monitor (connect several and each one is +a separate screen, like plugging in more monitors); **Mirror** shows a copy +of one of this computer's screens (the main one unless *Screen to mirror* in +Stream options says otherwise). Every display mirroring the same screen +shares one capture and one encode; each still gets its own encrypted stream. +From a shortcut: `silkscreen --connect surface --connect tablet`, or +`--mirror projector`. + +**Wi-Fi and falling behind.** On a slow or busy link frames can queue up +and the picture drifts behind the host. Two optional settings help: +*Display options → When the picture falls behind: Skip ahead after N ms* +(display side, or `silkscreen display --skip-behind 150`) drops the queued +frames and jumps back to live; *Stream options → Frame rate limit* (host side, +or `--max-fps 30`) sends fewer frames so the link keeps up, without changing +the screen's own refresh rate. The debug overlay shows how far behind the +picture is. + +Stream options change live while connected: bitrate, codec, encoder and +packet size switch in place; source, resolution, size and refresh rate +reconnect for a moment. + +Under **App** (both off until you turn them on): + +- **Show Silkscreen in the system tray** (the menu bar on macOS): closing the + window then keeps Silkscreen and its connection running. The icon's menu + shows the status and can disconnect, start or stop display mode, reopen + the window or quit. +- **Start Silkscreen when I log in**, opening the window, staying in the tray, + or going straight into display mode (handy for a computer that is always a + display). Uses the per-user Run key on Windows, a LaunchAgent on macOS and + `~/.config/autostart` on Linux. + ### Pairing and security Each device has its own key (shown as a fingerprint like `48c6-4328-caa5`). @@ -86,7 +138,7 @@ silkscreen paired # list paired devices | `host` | `--code 123456` | pairing code shown on the display (otherwise prompted) | | `host` | `--source monitor [--monitor DISPLAY3]` | stream an existing monitor, e.g. one with a **dummy HDMI/DP plug** | | `host` | `--resolution auto\|full\|half` | `auto` (default) streams panels above 4K at half resolution — a 4500×3000 Surface Studio as 2250×1500 — for low latency; `full` is sharpest but adds latency (also in the app under *Stream options*) | -| `host` | `--codec hevc\|h264\|av1`, `--bitrate 80` | override codec / Mbit/s | +| `host` | `--codec hevc\|h264\|av1\|pyrowave`, `--bitrate 80` | override codec / Mbit/s | | `host` | `--size 2560x1440 --refresh 60` | override the display's native mode | | `host` | `--mtu 8900` | larger UDP datagrams on a jumbo-frame Ethernet network | | `host` | `--source test-pattern` | animated pattern that draws touch points — tests everything except capture | @@ -172,12 +224,17 @@ Latency design: - **Zero-copy capture** on Linux and macOS: the compositor's buffer is converted directly to NV12 (parallel BT.709 converter, 2 ms at 13.5 MP) and the buffer is returned as soon as the encoder is done. - **Mailbox presentation** on the display: the newest frame replaces any queued one. - **HEVC by default**: most hardware H.264 encoders stop at 4096 px wide (too small for a 4500 px Surface Studio panel); Silkscreen falls back through the display's codec list automatically. +- **PyroWave (experimental, pick it in Stream options)**: [PyroWave](https://github.com/Themaister/pyrowave) is an intra-only wavelet codec in GPU compute shaders (Vulkan on Windows/Linux, Metal on Apple Silicon). Encode and decode take well under a millisecond and every frame stands alone, but it needs roughly 1 bit per pixel — several hundred Mbit/s — so it is for wired Ethernet. Both computers need a supported GPU; it is never chosen automatically. ## Building -Needs Rust (stable), clang, and FFmpeg 7.1+ development files (8.x tested). +Needs Rust (stable), clang, CMake (except on macOS), and FFmpeg 7.1+ +development files (8.x tested). The experimental PyroWave codec is built from +git submodules; fetch them first, or build with `--no-default-features` to +leave PyroWave out. ```sh +scripts/fetch-submodules.sh # Fedora: dnf install ffmpeg-devel pipewire-devel clang (ffmpeg-devel from RPM Fusion for x264/NVENC) # Ubuntu: apt install libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev \ # libavdevice-dev libswscale-dev libswresample-dev libpipewire-0.3-dev clang @@ -198,7 +255,7 @@ Workspace layout: | Crate | Purpose | |---|---| | `silkscreen-proto` | wire protocol: framed control messages, UDP packetizer/reassembler | -| `silkscreen-codec` | FFmpeg hardware encode/decode, BT.709 RGB→NV12 converter | +| `silkscreen-codec` | FFmpeg hardware encode/decode, PyroWave, BT.709 RGB→NV12 converter | | `silkscreen-platform` | per-OS virtual monitor, capture, input injection; test pattern | | `silkscreen-core` | shared by desktop and Android: identity and pairing, encryption, mDNS discovery, settings, the display's network service | | `silkscreen` | the desktop app: GUI (egui), CLI, host sessions, FFmpeg decoding, wgpu renderer | @@ -208,9 +265,11 @@ Workspace layout: Tested end to end on Linux (KDE Plasma 6.7, Wayland): virtual output creation, PipeWire capture, encode/decode, rendering, pointer and keyboard injection, -pairing, the GUI, hotkeys and the debug overlay. The Windows and -macOS backends compile against the real platform APIs but **have not yet been -run on those systems** — expect some first-run fixes there. +pairing, the GUI, hotkeys and the debug overlay. On macOS (Apple Silicon) the +host — virtual displays, several at once, mirroring, VideoToolbox and PyroWave +encoding — and the display have been run on real hardware. Windows has been run +as a display on a Surface (Intel graphics); Windows as a host, and Linux since +the latest changes, have not yet been tested on real hardware. Not done yet: - Pen pressure on Linux/macOS hosts (KWin's fake-input and CGEvent have no tablet API; the pen drives the pointer). @@ -222,4 +281,17 @@ Not done yet: ## License -MIT OR Apache-2.0. FFmpeg is used as a shared library under the LGPL. +Silkscreen is free and open source, licensed under either of + +- the MIT License ([LICENSE-MIT](LICENSE-MIT)), or +- the Apache License 2.0 ([LICENSE-APACHE](LICENSE-APACHE)), + +at your option. Contributions are accepted under the same terms. + +Silkscreen builds on FFmpeg (LGPL v3 in the Windows and Linux downloads; the +macOS app bundles Homebrew's GPL v3 build), PyroWave, Granite and volk (MIT), +the Vulkan headers, AndroidX/Jetpack Compose (Apache 2.0) and several hundred +Rust crates. Credits and license details are in +[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) and the full Rust crate +licenses in [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md); the downloads +include both. diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md new file mode 100644 index 0000000..059f203 --- /dev/null +++ b/THIRD-PARTY-LICENSES.md @@ -0,0 +1,8507 @@ +# Third-party Rust crates in Silkscreen + +Generated by [cargo-about](https://github.com/EmbarkStudios/cargo-about) from +the crates built into Silkscreen on Windows, macOS, Linux and Android +(regenerate with `scripts/third-party-licenses.sh`). Other bundled software +(FFmpeg, PyroWave and the parts of Granite it uses) is listed in +[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md). + +## Summary + +| License | Crates | +|---|---| +| MIT License | 413 | +| ISC License | 19 | +| Unicode License v3 | 19 | +| Apache License 2.0 | 15 | +| BSD 3-Clause "New" or "Revised" License | 4 | +| Boost Software License 1.0 | 2 | +| zlib License | 2 | +| BSD 2-Clause "Simplified" License | 1 | +| Mozilla Public License 2.0 | 1 | +| SIL Open Font License 1.1 | 1 | +| Ubuntu Font Licence v1.0 | 1 | +| The Unlicense | 1 | +| Do What The F*ck You Want To Public License | 1 | + +## License texts + +### Apache License 2.0 + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +Licenses for support code +------------------------- + +Parts of the TLS test suite are under the Go license. This code is not included +in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so +distributing code linked against BoringSSL does not trigger this license: + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +BoringSSL uses the Chromium test infrastructure to run a continuous build, +trybots etc. The scripts which manage this, and the script for generating build +metadata, are under the Chromium license. Distributing code linked against +BoringSSL does not trigger this license. + +Copyright 2015 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### Apache License 2.0 + +Used by: [moxcms 0.8.1](https://github.com/awxkee/moxcms.git), [pxfm 0.1.30](https://github.com/awxkee/pxfm) + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Radzivon Bartoshyk + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +``` + +### Apache License 2.0 + +Used by: [codespan-reporting 0.13.1](https://github.com/brendanzab/codespan), [self_cell 1.3.0](https://github.com/Voultapher/self_cell) + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +``` + +### Apache License 2.0 + +Used by: [unicode-general-category 1.1.0](https://github.com/yeslogic/unicode-general-category) + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +``` + +### Apache License 2.0 + +Used by: [gethostname 1.1.0](https://codeberg.org/swsnr/gethostname.rs.git), [ring 0.17.14](https://github.com/briansmith/ring) + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +``` + +### Apache License 2.0 + +Used by: [glutin_wgl_sys 0.6.1](https://github.com/rust-windowing/glutin) + +``` +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Kirill Chibisov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +``` + +### Apache License 2.0 + +Used by: [dpi 0.1.2](https://github.com/rust-windowing/winit), [winit 0.30.13](https://github.com/rust-windowing/winit) + +``` +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +``` + +### Apache License 2.0 + +Used by: [ab_glyph 0.2.32](https://github.com/alexheretic/ab-glyph), [ab_glyph_rasterizer 0.1.10](https://github.com/alexheretic/ab-glyph), [owned_ttf_parser 0.25.1](https://github.com/alexheretic/owned-ttf-parser), [spirv 0.4.0+sdk-1.4.341.0](https://github.com/gfx-rs/rspirv) + +``` +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +``` + +### BSD 2-Clause "Simplified" License + +Used by: [arrayref 0.3.9](https://github.com/droundy/arrayref) + +``` +Copyright (c) 2015 David Roundy +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### BSD 3-Clause "New" or "Revised" License + +Used by: [tiny-skia-path 0.11.4](https://github.com/RazrFalcon/tiny-skia/tree/master/path), [tiny-skia 0.11.4](https://github.com/RazrFalcon/tiny-skia) + +``` +Copyright (c) 2011 Google Inc. All rights reserved. +Copyright (c) 2020 Yevhenii Reizner All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### BSD 3-Clause "New" or "Revised" License + +Used by: [subtle 2.6.1](https://github.com/dalek-cryptography/subtle) + +``` +Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved. +Copyright (c) 2016-2024 Isis Agora Lovecruft. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### BSD 3-Clause "New" or "Revised" License + +Used by: [curve25519-dalek 4.1.3](https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek) + +``` +Copyright (c) . + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### Boost Software License 1.0 + +Used by: [error-code 3.4.0](https://github.com/DoumanAsh/error-code) + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### Boost Software License 1.0 + +Used by: [clipboard-win 5.4.1](https://github.com/DoumanAsh/clipboard-win) + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +/* Copyright (c) 2014, Intel Corporation. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef OPENSSL_HEADER_EC_ECP_NISTZ384_H +#define OPENSSL_HEADER_EC_ECP_NISTZ384_H + +#include "../../limbs/limbs.h" + +#define P384_LIMBS (384u / LIMB_BITS) + +typedef struct { + Limb X[P384_LIMBS]; + Limb Y[P384_LIMBS]; + Limb Z[P384_LIMBS]; +} P384_POINT; + +typedef struct { + Limb X[P384_LIMBS]; + Limb Y[P384_LIMBS]; +} P384_POINT_AFFINE; + + +#endif // OPENSSL_HEADER_EC_ECP_NISTZ384_H + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2015-2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! EdDSA Signatures. + +use super::ops::ELEM_LEN; +use crate::digest; + +pub mod signing; +pub mod verification; + +/// The length of an Ed25519 public key. +pub const ED25519_PUBLIC_KEY_LEN: usize = ELEM_LEN; + +pub fn eddsa_digest(signature_r: &[u8], public_key: &[u8], msg: &[u8]) -> digest::Digest { + let mut ctx = digest::Context::new(&digest::SHA512); + ctx.update(signature_r); + ctx.update(public_key); + ctx.update(msg); + ctx.finish() +} + +``` + +### ISC License + +Used by: [untrusted 0.9.0](https://github.com/briansmith/untrusted) + +``` +// Copyright 2015-2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2015-2022 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use crate::limb::Limb; + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct N0([Limb; 2]); + +impl N0 { + #[cfg(feature = "alloc")] + pub(super) const LIMBS_USED: usize = 64 / crate::limb::LIMB_BITS; + + #[inline] + pub const fn precalculated(n0: u64) -> Self { + #[cfg(target_pointer_width = "64")] + { + Self([n0, 0]) + } + + #[cfg(target_pointer_width = "32")] + { + Self([n0 as Limb, (n0 >> crate::limb::LIMB_BITS) as Limb]) + } + } +} + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2015-2025 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use crate::{bb, error}; + +#[deprecated( + note = "To be removed. Internal function not intended for external use with no promises regarding side channels." +)] +pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<(), error::Unspecified> { + bb::verify_slices_are_equal(a, b) +} + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Elliptic curve operations and schemes using Curve25519. + +pub mod ed25519; + +pub mod x25519; + +mod ops; +mod scalar; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2016-2024 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use crate::error::{KeyRejected, Unspecified}; + +impl From for Unspecified { + fn from(source: untrusted::EndOfInput) -> Self { + super::erase(source) + } +} + +impl From for Unspecified { + fn from(source: core::array::TryFromSliceError) -> Self { + super::erase(source) + } +} + +impl From for Unspecified { + fn from(source: KeyRejected) -> Self { + super::erase(source) + } +} + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2018 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Serialization and deserialization. + +#[doc(hidden)] +pub mod der; + +#[cfg(feature = "alloc")] +mod writer; + +#[cfg(feature = "alloc")] +pub(crate) mod der_writer; + +pub(crate) mod positive; + +pub use self::positive::Positive; + +#[cfg(feature = "alloc")] +pub(crate) use self::writer::TooLongError; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2019-2024 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use super::BlockLen; + +pub(super) use self::{ + sha2_32::{block_data_order_32, State32, SHA256_BLOCK_LEN}, + sha2_64::{block_data_order_64, State64, SHA512_BLOCK_LEN}, +}; + +pub(super) const CHAINING_WORDS: usize = 8; + +#[cfg(any( + all(target_arch = "aarch64", target_endian = "little"), + all(target_arch = "arm", target_endian = "little"), + target_arch = "x86_64" +))] +#[macro_use] +mod ffi; + +pub(super) mod fallback; +mod sha2_32; +mod sha2_64; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2024 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// TODO(MSRV 1.76): Replace with `core::ptr::from_mut`. +#[allow(dead_code)] +#[inline(always)] +pub fn from_mut(r: &mut T) -> *mut T { + r +} + +// TODO(MSRV 1.76): Replace with `core::ptr::from_ref`. +#[allow(dead_code)] +#[inline(always)] +pub const fn from_ref(r: &T) -> *const T { + r +} + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2024 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Integration tests for non-public APIs. + +mod bits_tests; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2024 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +pub use self::{ + array::Array, + base::{IndexError, Overlapping}, + partial_block::PartialBlock, +}; + +mod array; +mod base; +mod partial_block; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2025 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#![cfg(all(target_arch = "aarch64", target_endian = "little"))] + +pub(in super::super) mod mont; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2025 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#![cfg(target_arch = "x86_64")] + +pub(in super::super::super) mod mont; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2025 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +mod storage; + +pub(super) use self::storage::{AlignedStorage, LIMBS_PER_CHUNK}; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +// Copyright 2025 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +pub(super) mod aarch64; +pub(super) mod x86_64; + +``` + +### ISC License + +Used by: [ring 0.17.14](https://github.com/briansmith/ring) + +``` +Copyright 2015-2025 Brian Smith. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +### ISC License + +Used by: [libloading 0.8.9](https://github.com/nagisa/rust_libloading/), [libloading 0.9.0](https://github.com/nagisa/rust_libloading/) + +``` +Copyright © 2015, Simonas Kazlauskas + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without +fee is hereby granted, provided that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +### MIT License + +Used by: [uds_windows 1.2.1](https://github.com/haraldh/rust_uds_windows) + +``` + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + +``` + +### MIT License + +Used by: [atomic-waker 1.1.2](https://github.com/smol-rs/atomic-waker), [futures-lite 2.6.1](https://github.com/smol-rs/futures-lite) + +``` +=============================================================================== + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=============================================================================== + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [sha2 0.10.9](https://github.com/RustCrypto/hashes) + +``` +Copyright (c) 2006-2009 Graydon Hoare +Copyright (c) 2009-2013 Mozilla Foundation +Copyright (c) 2016 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [rayon-core 1.13.0](https://github.com/rayon-rs/rayon), [rayon 1.12.0](https://github.com/rayon-rs/rayon) + +``` +Copyright (c) 2010 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [core-foundation-sys 0.8.7](https://github.com/servo/core-foundation-rs), [core-foundation 0.9.4](https://github.com/servo/core-foundation-rs), [core-graphics-types 0.1.3](https://github.com/servo/core-foundation-rs), [core-graphics 0.23.2](https://github.com/servo/core-foundation-rs), [euclid 0.22.14](https://github.com/servo/euclid) + +``` +Copyright (c) 2012-2013 Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [hex 0.4.3](https://github.com/KokaKiwi/rust-hex) + +``` +Copyright (c) 2013-2014 The Rust Project Developers. +Copyright (c) 2015-2020 The rust-hex Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [form_urlencoded 1.2.2](https://github.com/servo/rust-url) + +``` +Copyright (c) 2013-2016 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [idna 1.1.0](https://github.com/servo/rust-url/), [percent-encoding 2.3.2](https://github.com/servo/rust-url/), [url 2.5.8](https://github.com/servo/rust-url) + +``` +Copyright (c) 2013-2025 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [cfg-if 1.0.5](https://github.com/rust-lang/cfg-if), [scoped-tls 1.0.1](https://github.com/alexcrichton/scoped-tls), [socket2 0.6.5](https://github.com/rust-lang/socket2) + +``` +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [mio 1.2.4](https://github.com/tokio-rs/mio) + +``` +Copyright (c) 2014 Carl Lerche and other MIO contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [errno 0.3.14](https://github.com/lambda-fairy/rust-errno) + +``` +Copyright (c) 2014 Chris Wong + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [bitflags 1.3.2](https://github.com/bitflags/bitflags), [bitflags 2.13.2](https://github.com/bitflags/bitflags), [log 0.4.34](https://github.com/rust-lang/log), [num-traits 0.2.19](https://github.com/rust-num/num-traits), [regex-automata 0.4.18](https://github.com/rust-lang/regex), [regex-syntax 0.8.11](https://github.com/rust-lang/regex) + +``` +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [uuid 1.28.0](https://github.com/uuid-rs/uuid) + +``` +Copyright (c) 2014 The Rust Project Developers +Copyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlon + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [nom 8.0.0](https://github.com/rust-bakery/nom) + +``` +Copyright (c) 2014-2019 Geoffroy Couprie + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [flate2 1.1.10](https://github.com/rust-lang/flate2-rs) + +``` +Copyright (c) 2014-2026 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [either 1.19.0](https://github.com/rayon-rs/either), [itertools 0.15.0](https://github.com/rust-itertools/itertools), [petgraph 0.8.3](https://github.com/petgraph/petgraph) + +``` +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [wayland-backend 0.3.17](https://github.com/smithay/wayland-rs), [wayland-client 0.31.15](https://github.com/smithay/wayland-rs), [wayland-cursor 0.31.14](https://github.com/smithay/wayland-rs), [wayland-protocols-experimental 20250721.0.1](https://github.com/smithay/wayland-rs), [wayland-protocols-misc 0.3.12](https://github.com/smithay/wayland-rs), [wayland-protocols-plasma 0.3.12](https://github.com/smithay/wayland-rs), [wayland-protocols-wlr 0.3.12](https://github.com/smithay/wayland-rs), [wayland-protocols 0.32.13](https://github.com/smithay/wayland-rs), [wayland-scanner 0.31.11](https://github.com/smithay/wayland-rs), [wayland-sys 0.31.11](https://github.com/smithay/wayland-rs) + +``` +Copyright (c) 2015 Elinor Berger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [ordered-float 5.5.0](https://github.com/reem/rust-ordered-float) + +``` +Copyright (c) 2015 Jonathan Reem + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [tempfile 3.27.0](https://github.com/Stebalien/tempfile) + +``` +Copyright (c) 2015 Steven Allen + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [heck 0.5.0](https://github.com/withoutboats/heck), [unicode-properties 0.1.4](https://github.com/unicode-rs/unicode-properties), [unicode-segmentation 1.13.3](https://github.com/unicode-rs/unicode-segmentation), [unicode-width 0.2.2](https://github.com/unicode-rs/unicode-width) + +``` +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [cobs 0.3.0](https://github.com/jamesmunns/cobs.rs) + +``` +Copyright (c) 2015 The cobs.rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [quick-error 2.0.1](http://github.com/tailhook/quick-error) + +``` +Copyright (c) 2015 The quick-error Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [jni-sys 0.3.1](https://github.com/jni-rs/jni-sys), [jni-sys 0.4.1](https://github.com/jni-rs/jni-sys) + +``` +Copyright (c) 2015 The rust-jni-sys Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [dlib 0.5.3](https://github.com/elinorbgr/dlib) + +``` +Copyright (c) 2015 Victor Berger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +### MIT License + +Used by: [png 0.18.1](https://github.com/image-rs/image-png) + +``` +Copyright (c) 2015 nwin + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [blake2 0.10.6](https://github.com/RustCrypto/hashes) + +``` +Copyright (c) 2015-2016 The blake2-rfc Developers, Cesar Barros +Copyright (c) 2017 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [fixedbitset 0.5.7](https://github.com/petgraph/fixedbitset) + +``` +Copyright (c) 2015-2017 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [hkdf 0.12.4](https://github.com/RustCrypto/KDFs/) + +``` +Copyright (c) 2015-2018 Vlad Filippov +Copyright (c) 2018-2021 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [poly1305 0.8.0](https://github.com/RustCrypto/universal-hashes) + +``` +Copyright (c) 2015-2019 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [ash 0.38.0+1.3.281](https://github.com/ash-rs/ash) + +``` +Copyright (c) 2016 ASH + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [futures-channel 0.3.34](https://github.com/rust-lang/futures-rs), [futures-core 0.3.34](https://github.com/rust-lang/futures-rs), [futures-io 0.3.34](https://github.com/rust-lang/futures-rs), [futures-macro 0.3.34](https://github.com/rust-lang/futures-rs), [futures-sink 0.3.34](https://github.com/rust-lang/futures-rs), [futures-task 0.3.34](https://github.com/rust-lang/futures-rs), [futures-util 0.3.34](https://github.com/rust-lang/futures-rs) + +``` +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [hashbrown 0.15.5](https://github.com/rust-lang/hashbrown), [hashbrown 0.16.1](https://github.com/rust-lang/hashbrown), [hashbrown 0.17.1](https://github.com/rust-lang/hashbrown) + +``` +Copyright (c) 2016 Amanieu d'Antras + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [utf8parse 0.2.2](https://github.com/alacritty/vte) + +``` +Copyright (c) 2016 Joe Wilm + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [lock_api 0.4.14](https://github.com/Amanieu/parking_lot), [parking_lot 0.12.5](https://github.com/Amanieu/parking_lot), [parking_lot_core 0.9.12](https://github.com/Amanieu/parking_lot), [thread_local 1.1.10](https://github.com/Amanieu/thread_local-rs) + +``` +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [indexmap 2.14.2](https://github.com/indexmap-rs/indexmap) + +``` +Copyright (c) 2016--2017 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [equivalent 1.0.3](https://github.com/indexmap-rs/equivalent) + +``` +Copyright (c) 2016--2023 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [scopeguard 1.2.0](https://github.com/bluss/scopeguard) + +``` +Copyright (c) 2016-2019 Ulrik Sverdrup "bluss" and scopeguard developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [cipher 0.4.4](https://github.com/RustCrypto/traits) + +``` +Copyright (c) 2016-2020 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [digest 0.10.7](https://github.com/RustCrypto/traits), [hmac 0.12.1](https://github.com/RustCrypto/MACs) + +``` +Copyright (c) 2017 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [memoffset 0.9.1](https://github.com/Gilnaa/memoffset) + +``` +Copyright (c) 2017 Gilad Naaman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +### MIT License + +Used by: [heapless 0.7.17](https://github.com/japaric/heapless) + +``` +Copyright (c) 2017 Jorge Aparicio + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [enumflags2_derive 0.7.12](https://github.com/meithecatte/enumflags2) + +``` +Copyright (c) 2017 Maik Klein + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [keyboard-types 0.8.3](https://github.com/rust-windowing/keyboard-types) + +``` +Copyright (c) 2017 Pyfisch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +### MIT License + +Used by: [stable_deref_trait 1.2.1](https://github.com/storyyeller/stable_deref_trait) + +``` +Copyright (c) 2017 Robert Grosse + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +``` + +### MIT License + +Used by: [foreign-types-macros 0.2.4](https://github.com/sfackler/foreign-types), [foreign-types-shared 0.3.1](https://github.com/sfackler/foreign-types), [foreign-types 0.5.0](https://github.com/sfackler/foreign-types) + +``` +Copyright (c) 2017 The foreign-types Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [arc-swap 1.9.3](https://github.com/vorner/arc-swap) + +``` +Copyright (c) 2017 arc-swap developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [signal-hook-registry 1.4.8](https://github.com/vorner/signal-hook) + +``` +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [enumflags2 0.7.12](https://github.com/meithecatte/enumflags2) + +``` +Copyright (c) 2017-2023 Maik Klein, Maja Kądziołka + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [aes 0.8.4](https://github.com/RustCrypto/block-ciphers) + +``` +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [bytes 1.12.1](https://github.com/tokio-rs/bytes) + +``` +Copyright (c) 2018 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [hash32 0.2.1](https://github.com/japaric/hash32) + +``` +Copyright (c) 2018 Jorge Aparicio + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [smithay-clipboard 0.7.3](https://github.com/smithay/smithay-clipboard) + +``` +Copyright (c) 2018 Lucas Timmins & Victor Berger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [fearless_simd 0.4.1](https://github.com/linebender/fearless_simd), [kurbo 0.13.1](https://github.com/linebender/kurbo), [peniko 0.6.1](https://github.com/linebender/peniko), [polycool 0.4.0](https://github.com/linebender/kurbo) + +``` +Copyright (c) 2018 Raph Levien + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [smallvec 1.16.3](https://github.com/servo/rust-smallvec) + +``` +Copyright (c) 2018 The Servo Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [ahash 0.8.12](https://github.com/tkaitchuck/ahash) + +``` +Copyright (c) 2018 Tom Kaitchuck + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [smithay-client-toolkit 0.19.2](https://github.com/smithay/client-toolkit), [smithay-client-toolkit 0.20.0](https://github.com/smithay/client-toolkit) + +``` +Copyright (c) 2018 Victor Berger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +### MIT License + +Used by: [calloop 0.13.0](https://github.com/Smithay/calloop), [calloop 0.14.5](https://github.com/Smithay/calloop) + +``` +Copyright (c) 2018 Victor Berger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [ttf-parser 0.25.1](https://github.com/harfbuzz/ttf-parser) + +``` +Copyright (c) 2018 Yevhenii Reizner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +``` + +### MIT License + +Used by: [directories 6.0.0](https://github.com/soc/directories-rs) + +``` +Copyright (c) 2018 directories-rs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [block-buffer 0.10.4](https://github.com/RustCrypto/utils) + +``` +Copyright (c) 2018-2019 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [dirs-sys 0.5.0](https://github.com/dirs-dev/dirs-sys-rs) + +``` +Copyright (c) 2018-2019 dirs-rs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [ctr 0.9.2](https://github.com/RustCrypto/block-modes) + +``` +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [opaque-debug 0.3.1](https://github.com/RustCrypto/utils) + +``` +Copyright (c) 2018-2024 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [getrandom 0.2.17](https://github.com/rust-random/getrandom) + +``` +Copyright (c) 2018-2024 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [getrandom 0.3.4](https://github.com/rust-random/getrandom) + +``` +Copyright (c) 2018-2025 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [rand_core 0.10.1](https://github.com/rust-random/rand_core) + +``` +Copyright (c) 2018-2026 The Rand Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [zeroize 1.9.1](https://github.com/RustCrypto/utils) + +``` +Copyright (c) 2018-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [getrandom 0.4.3](https://github.com/rust-random/getrandom) + +``` +Copyright (c) 2018-2026 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [postcard 1.1.3](https://github.com/jamesmunns/postcard) + +``` +Copyright (c) 2019 Anthony James Munns + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [slab 0.4.12](https://github.com/tokio-rs/slab) + +``` +Copyright (c) 2019 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [sharded-slab 0.1.7](https://github.com/hawkw/sharded-slab) + +``` +Copyright (c) 2019 Eliza Weisman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [matchers 0.2.0](https://github.com/hawkw/matchers) + +``` +Copyright (c) 2019 Eliza Weisman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [presser 0.3.1](https://github.com/EmbarkStudios/presser) + +``` +Copyright (c) 2019 Embark Studios + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [font-types 0.12.7](https://github.com/googlefonts/fontations), [read-fonts 0.41.0](https://github.com/googlefonts/fontations), [skrifa 0.44.0](https://github.com/googlefonts/fontations) + +``` +Copyright (c) 2019 Fontations Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [wl-clipboard-rs 0.9.4](https://github.com/YaLTeR/wl-clipboard-rs) + +``` +Copyright (c) 2019 Ivan Molodetskikh + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [ghash 0.5.1](https://github.com/RustCrypto/universal-hashes) + +``` +Copyright (c) 2019 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [aes-gcm 0.10.3](https://github.com/RustCrypto/AEADs), [chacha20poly1305 0.10.1](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305) + +``` +Copyright (c) 2019 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [aead 0.5.2](https://github.com/RustCrypto/traits) + +``` +Copyright (c) 2019 The RustCrypto Project Developers +Copyright (c) 2019 MobileCoin, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [tracing-attributes 0.1.31](https://github.com/tokio-rs/tracing), [tracing-core 0.1.36](https://github.com/tokio-rs/tracing), [tracing-log 0.2.0](https://github.com/tokio-rs/tracing), [tracing-subscriber 0.3.23](https://github.com/tokio-rs/tracing), [tracing 0.1.44](https://github.com/tokio-rs/tracing) + +``` +Copyright (c) 2019 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [universal-hash 0.5.1](https://github.com/RustCrypto/traits) + +``` +Copyright (c) 2019-2020 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [polyval 0.6.2](https://github.com/RustCrypto/universal-hashes) + +``` +Copyright (c) 2019-2023 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [chacha20 0.9.1](https://github.com/RustCrypto/stream-ciphers) + +``` +Copyright (c) 2019-2023 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [chacha20 0.10.2](https://github.com/RustCrypto/stream-ciphers) + +``` +Copyright (c) 2019-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [downcast-rs 1.2.1](https://github.com/marcianx/downcast-rs) + +``` +Copyright (c) 2020 Ashish Myles and contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [document-features 0.2.12](https://github.com/slint-ui/document-features) + +``` +Copyright (c) 2020 Olivier Goffart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [litrs 1.0.1](https://github.com/LukasKalbertodt/litrs) + +``` +Copyright (c) 2020 Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [memmap2 0.9.11](https://github.com/RazrFalcon/memmap2-rs) + +``` +Copyright (c) 2020 Yevhenii Reizner +Copyright (c) 2015 Dan Burkert + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [pollster 1.0.1](https://github.com/zesterer/pollster) + +``` +Copyright (c) 2020-2021 Joshua Barretto + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [cpufeatures 0.2.17](https://github.com/RustCrypto/utils) + +``` +Copyright (c) 2020-2025 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [cpufeatures 0.3.1](https://github.com/RustCrypto/utils) + +``` +Copyright (c) 2020-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [crypto-common 0.1.7](https://github.com/RustCrypto/traits) + +``` +Copyright (c) 2021 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [gpu-allocator 0.28.0](https://github.com/Traverse-Research/gpu-allocator) + +``` +Copyright (c) 2021 Traverse Research B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +### MIT License + +Used by: [renderdoc-sys 1.1.0](https://github.com/ebkalderon/renderdoc-rs) + +``` +Copyright (c) 2022 Eyal Kalderon + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [inout 0.1.4](https://github.com/RustCrypto/utils) + +``` +Copyright (c) 2022 The RustCrypto Project Developers +Copyright (c) 2022 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [strict-num 0.1.1](https://github.com/RazrFalcon/strict-num) + +``` +Copyright (c) 2022 Yevhenii Reizner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [xkeysym 0.2.1](https://github.com/notgull/xkeysym) + +``` +Copyright (c) 2022-2023 John Nunley + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [calloop-wayland-source 0.3.0](https://github.com/smithay/calloop-wayland-source), [calloop-wayland-source 0.4.1](https://github.com/smithay/calloop-wayland-source) + +``` +Copyright (c) 2023 Kirill Chibisov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +### MIT License + +Used by: [cursor-icon 1.2.0](https://github.com/rust-windowing/cursor-icon), [wayland-csd-frame 0.3.0](https://github.com/rust-windowing/wayland-csd-frame) + +``` +Copyright (c) 2023 Kirill Chibisov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [bit-vec 0.9.1](https://github.com/contain-rs/bit-vec) + +``` +Copyright (c) 2023 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [zbus 5.19.0](https://github.com/z-galaxy/zbus/), [zbus_macros 5.19.0](https://github.com/z-galaxy/zbus/), [zbus_names 4.3.5](https://github.com/z-galaxy/zbus/), [zvariant 5.15.1](https://github.com/z-galaxy/zbus/), [zvariant_derive 5.15.1](https://github.com/z-galaxy/zbus/) + +``` +Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [bit-set 0.10.0](https://github.com/contain-rs/bit-set) + +``` +Copyright (c) 2026 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [zcheapstr 1.1.0](https://github.com/z-galaxy/zcheapstr/) + +``` +Copyright (c) 2026 Zeeshan Ali Khan & zcheapstr contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [anstream 1.0.0](https://github.com/rust-cli/anstyle.git), [anstyle-parse 1.0.0](https://github.com/rust-cli/anstyle.git), [anstyle-query 1.1.5](https://github.com/rust-cli/anstyle.git), [anstyle-wincon 3.0.11](https://github.com/rust-cli/anstyle.git), [anstyle 1.0.14](https://github.com/rust-cli/anstyle.git), [clap 4.6.7](https://github.com/clap-rs/clap), [clap_builder 4.6.7](https://github.com/clap-rs/clap), [clap_derive 4.6.7](https://github.com/clap-rs/clap), [clap_lex 1.1.1](https://github.com/clap-rs/clap), [colorchoice 1.0.5](https://github.com/rust-cli/anstyle.git), [is_terminal_polyfill 1.70.2](https://github.com/polyfill-rs/is_terminal_polyfill), [once_cell_polyfill 1.70.2](https://github.com/polyfill-rs/once_cell_polyfill), [toml_datetime 1.1.2+spec-1.1.0](https://github.com/toml-rs/toml), [toml_edit 0.25.17+spec-1.1.0](https://github.com/toml-rs/toml), [toml_parser 1.1.5+spec-1.1.0](https://github.com/toml-rs/toml) + +``` +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [libc 0.2.191](https://github.com/rust-lang/libc) + +``` +Copyright (c) The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [idna_adapter 1.2.2](https://github.com/hsivonen/idna_adapter) + +``` +Copyright (c) The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [arrayvec 0.7.8](https://github.com/bluss/arrayvec) + +``` +Copyright (c) Ulrik Sverdrup "bluss" 2015-2023 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [synstructure 0.14.0](https://github.com/mystor/synstructure) + +``` +Copyright 2016 Nika Layzell + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [rand 0.10.3](https://github.com/rust-random/rand), [rand_core 0.6.4](https://github.com/rust-random/rand) + +``` +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [if-addrs 0.15.0](https://github.com/messense/if-addrs) + +``` +Copyright 2018 MaidSafe.net limited. +Copyright 2020 messense + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [nohash-hasher 0.2.0](https://github.com/paritytech/nohash-hasher) + +``` +Copyright 2018 Parity Technologies (UK) Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [as-raw-xcb-connection 1.0.1](https://github.com/psychon/as-raw-xcb-connection) + +``` +Copyright 2019 as-raw-xcb-connection Contributers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [x11rb-protocol 0.13.2](https://github.com/psychon/x11rb), [x11rb 0.13.2](https://github.com/psychon/x11rb) + +``` +Copyright 2019 x11rb Contributers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [vello_common 0.2.0](https://github.com/linebender/vello), [vello_cpu 0.2.0](https://github.com/linebender/vello) + +``` +Copyright 2020 the Vello Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [zerocopy-derive 0.8.63](https://github.com/google/zerocopy), [zerocopy 0.8.63](https://github.com/google/zerocopy) + +``` +Copyright 2023 The Fuchsia Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +``` + +### MIT License + +Used by: [utf8_iter 1.0.4](https://github.com/hsivonen/utf8_iter) + +``` +Copyright Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [libspa-sys 0.10.1](https://gitlab.freedesktop.org/pipewire/pipewire-rs), [libspa 0.10.1](https://gitlab.freedesktop.org/pipewire/pipewire-rs), [pipewire-sys 0.10.1](https://gitlab.freedesktop.org/pipewire/pipewire-rs), [pipewire 0.10.1](https://gitlab.freedesktop.org/pipewire/pipewire-rs) + +``` +Copyright The pipewire-rs Contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [wayland-protocols 0.32.13](https://github.com/smithay/wayland-rs) + +``` +Copyright © 2008-2013 Kristian Høgsberg +Copyright © 2010-2013 Intel Corporation +Copyright © 2013 Rafael Antognolli +Copyright © 2013 Jasper St. Pierre +Copyright © 2014 Jonas Ådahl +Copyright © 2014 Jason Ekstrand +Copyright © 2014-2015 Collabora, Ltd. +Copyright © 2015 Red Hat Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +--- + +The above is the version of the MIT "Expat" License used by X.org: + + http://cgit.freedesktop.org/xorg/xserver/tree/COPYING + +``` + +### MIT License + +Used by: [fax 0.2.7](https://github.com/pdf-rs/fax) + +``` +Copyright © 2021 The pdf-rs contributers. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [epaint_default_fonts 0.37.0](https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts) + +``` +MIT License + +Copyright (c) 2014 John Slegers + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [tree_magic_mini 3.2.2](https://github.com/mbrubeck/tree_magic/) + +``` +MIT License + +Copyright (c) 2017 Aaron Hancock + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [static_assertions 1.1.0](https://github.com/nvzqz/static-assertions-rs) + +``` +MIT License + +Copyright (c) 2017 Nikolai Vazquez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [spake2 0.4.0](https://github.com/RustCrypto/PAKEs/tree/master/spake2) + +``` +MIT License + +Copyright (c) 2017-2023 Brian Warner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [tiff 0.11.3](https://github.com/image-rs/image-tiff) + +``` +MIT License + +Copyright (c) 2018 PistonDevelopers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [crc32fast 1.5.3](https://github.com/srijs/rust-crc32fast) + +``` +MIT License + +Copyright (c) 2018 Sam Rijs, Alex Crichton and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [bytemuck 1.25.2](https://github.com/Lokathor/bytemuck), [bytemuck_derive 1.12.1](https://github.com/Lokathor/bytemuck) + +``` +MIT License + +Copyright (c) 2019 Daniel "Lokathor" Gee. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [raw-window-handle 0.6.2](https://github.com/rust-windowing/raw-window-handle) + +``` +MIT License + +Copyright (c) 2019 Osspial + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [tokio-macros 2.7.2](https://github.com/tokio-rs/tokio) + +``` +MIT License + +Copyright (c) 2019 Yoshua Wuyts +Copyright (c) Tokio Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [ashpd 0.13.13](https://github.com/bilelmoussaoui/ashpd) + +``` +MIT License + +Copyright (c) 2020 Bilal Elmoussaoui + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [android-properties 0.2.2](https://github.com/miklelappo/android-properties) + +``` +MIT License + +Copyright (c) 2020 Mikhail Lappo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [xcursor 0.3.11](https://github.com/esposm03/xcursor-rs) + +``` +MIT License + +Copyright (c) 2020 Samuele Esposito + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [snow 0.10.0](https://github.com/mcginty/snow) + +``` +MIT License + +Copyright (c) 2021 Jake McGinty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [mdns-sd 0.21.6](https://github.com/keepsimple1/mdns-sd) + +``` +MIT License + +Copyright (c) 2021-2022, Han Xu, keepsimple@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [sctk-adwaita 0.10.1](https://github.com/PolyMeilex/sctk-adwaita) + +``` +MIT License + +Copyright (c) 2022 Bartłomiej Maryńczak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [arboard 3.7.0](https://github.com/1Password/arboard) + +``` +MIT License + +Copyright (c) 2022 The Arboard contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [muda 0.21.2](https://github.com/tauri-apps/muda), [tray-icon 0.26.1](https://github.com/tauri-apps/tray-icon) + +``` +MIT License + +Copyright (c) 2022-2022 Tauri Programme within The Commons Conservancy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [range-alloc 0.1.5](https://github.com/gfx-rs/range-alloc) + +``` +MIT License + +Copyright (c) 2023 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [web-time 1.1.0](https://github.com/daxpedda/web-time) + +``` +MIT License + +Copyright (c) 2023 dAxpeDDa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [task-local 0.1.1](https://github.com/BugenZhao/task-local) + +``` +MIT License + +Copyright (c) 2025 Bugen Zhao + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [socket-pktinfo 0.4.1](https://github.com/pixsper/socket-pktinfo) + +``` +MIT License + +Copyright (c) 2025 Pixsper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [naga-types 30.0.1](https://github.com/gfx-rs/wgpu), [naga 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu-core-deps-apple 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu-core-deps-windows-linux-android 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu-core 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu-hal 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu-naga-bridge 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu-types 30.0.1](https://github.com/gfx-rs/wgpu), [wgpu 30.0.1](https://github.com/gfx-rs/wgpu) + +``` +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [silkscreen 0.2.0](https://crates.io/crates/silkscreen), [silkscreen-android 0.2.0](https://crates.io/crates/silkscreen-android), [silkscreen-codec 0.2.0](https://crates.io/crates/silkscreen-codec), [silkscreen-core 0.2.0](https://crates.io/crates/silkscreen-core), [silkscreen-platform 0.2.0](https://crates.io/crates/silkscreen-platform), [silkscreen-proto 0.2.0](https://crates.io/crates/silkscreen-proto), [accesskit 0.25.1](https://github.com/AccessKit/accesskit), [block2 0.5.1](https://github.com/madsmtm/objc2), [block2 0.6.2](https://github.com/madsmtm/objc2), [cesu8 1.1.0](https://github.com/emk/cesu8-rs), [cookie-factory 0.3.3](https://github.com/rust-bakery/cookie-factory), [dispatch2 0.3.1](https://github.com/madsmtm/objc2), [dispatch 0.2.0](http://github.com/SSheldon/rust-dispatch), [dpi 0.1.2](https://github.com/rust-windowing/winit), [ecolor 0.37.0](https://github.com/emilk/egui), [egui-wgpu 0.37.0](https://github.com/emilk/egui/tree/main/crates/egui-wgpu), [egui-winit 0.37.0](https://github.com/emilk/egui/tree/main/crates/egui-winit), [egui 0.37.0](https://github.com/emilk/egui), [emath 0.37.0](https://github.com/emilk/egui/tree/main/crates/emath), [epaint 0.37.0](https://github.com/emilk/egui/tree/main/crates/epaint), [jni-macros 0.22.4](https://github.com/jni-rs/jni-rs), [jni-sys-macros 0.4.1](https://github.com/jni-rs/jni-sys), [jni 0.22.4](https://github.com/jni-rs/jni-rs), [libm 0.2.16](https://github.com/rust-lang/compiler-builtins), [ndk-context 0.1.1](https://github.com/rust-windowing/android-ndk-rs), [ndk-sys 0.5.0+25.2.9519653](https://github.com/rust-mobile/ndk), [ndk-sys 0.6.0+11769913](https://github.com/rust-mobile/ndk), [ndk 0.9.0](https://github.com/rust-mobile/ndk), [objc-sys 0.3.5](https://github.com/madsmtm/objc2), [objc2-app-kit 0.2.2](https://github.com/madsmtm/objc2), [objc2-app-kit 0.3.2](https://github.com/madsmtm/objc2), [objc2-av-foundation 0.3.2](https://github.com/madsmtm/objc2), [objc2-cloud-kit 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-audio-types 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-audio 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-data 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-foundation 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-graphics 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-image 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-media 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-text 0.3.2](https://github.com/madsmtm/objc2), [objc2-core-video 0.3.2](https://github.com/madsmtm/objc2), [objc2-encode 4.1.0](https://github.com/madsmtm/objc2), [objc2-foundation 0.2.2](https://github.com/madsmtm/objc2), [objc2-foundation 0.3.2](https://github.com/madsmtm/objc2), [objc2-metal 0.3.2](https://github.com/madsmtm/objc2), [objc2-quartz-core 0.3.2](https://github.com/madsmtm/objc2), [objc2-screen-capture-kit 0.3.2](https://github.com/madsmtm/objc2), [objc2-uniform-type-identifiers 0.3.2](https://github.com/madsmtm/objc2), [objc2 0.5.3](https://github.com/madsmtm/objc2), [objc2 0.6.5](https://github.com/madsmtm/objc2), [profiling 1.0.18](https://github.com/aclysma/profiling), [type-map 0.5.1](https://github.com/kardeiz/type-map), [windows-collections 0.3.2](https://github.com/microsoft/windows-rs), [windows-core 0.62.2](https://github.com/microsoft/windows-rs), [windows-future 0.3.2](https://github.com/microsoft/windows-rs), [windows-implement 0.60.2](https://github.com/microsoft/windows-rs), [windows-interface 0.59.3](https://github.com/microsoft/windows-rs), [windows-link 0.2.1](https://github.com/microsoft/windows-rs), [windows-numerics 0.3.1](https://github.com/microsoft/windows-rs), [windows-result 0.4.1](https://github.com/microsoft/windows-rs), [windows-strings 0.5.1](https://github.com/microsoft/windows-rs), [windows-sys 0.45.0](https://github.com/microsoft/windows-rs), [windows-sys 0.52.0](https://github.com/microsoft/windows-rs), [windows-sys 0.59.0](https://github.com/microsoft/windows-rs), [windows-sys 0.61.2](https://github.com/microsoft/windows-rs), [windows-targets 0.42.2](https://github.com/microsoft/windows-rs), [windows-targets 0.52.6](https://github.com/microsoft/windows-rs), [windows-threading 0.2.1](https://github.com/microsoft/windows-rs), [windows 0.62.2](https://github.com/microsoft/windows-rs), [windows_x86_64_gnu 0.52.6](https://github.com/microsoft/windows-rs), [windows_x86_64_msvc 0.42.2](https://github.com/microsoft/windows-rs), [windows_x86_64_msvc 0.52.6](https://github.com/microsoft/windows-rs) + +``` +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [tokio 1.54.0](https://github.com/tokio-rs/tokio) + +``` +MIT License + +Copyright (c) Tokio Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [simd-adler32 0.3.10](https://github.com/mcountryman/simd-adler32) + +``` +MIT License + +Copyright (c) [2021] [Marvin Countryman] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [zune-core 0.5.3](https://github.com/etemesi254/zune-image), [zune-jpeg 0.5.15](https://github.com/etemesi254/zune-image/tree/dev/crates/zune-jpeg) + +``` +MIT License + +Copyright (c) zune-image developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [miniz_oxide 0.8.9](https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide), [miniz_oxide 0.9.1](https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide) + +``` +MIT License + +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC +Copyright (c) 2017 Frommi +Copyright (c) 2017-2024 oyvindln + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [miniz_oxide 0.8.9](https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide), [miniz_oxide 0.9.1](https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide) + +``` +MIT License + +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC +Copyright (c) 2017 Frommi +Copyright (c) 2017-2024 oyvindln + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [fdeflate 0.3.7](https://github.com/image-rs/fdeflate), [image 0.25.10](https://github.com/image-rs/image) + +``` +MIT License + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [simdutf8 0.1.5](https://github.com/rusticstuff/simdutf8) + +``` +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +### MIT License + +Used by: [android-activity 0.6.1](https://github.com/rust-mobile/android-activity), [color 0.3.3](https://github.com/linebender/color), [half 2.7.1](https://github.com/VoidStarKat/half-rs), [linebender_resource_handle 0.1.1](https://github.com/linebender/raw_resource_handle) + +``` +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [paranoid-android 0.2.2](https://github.com/raftario/tracing-android) + +``` +MIT License Copyright (c) 2021 Raphaël Thériault + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [wayland-protocols-plasma 0.3.12](https://github.com/smithay/wayland-rs) + +``` +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [flume 0.12.0](https://github.com/zesterer/flume), [rustc-hash 2.1.3](https://github.com/rust-lang/rustc-hash) + +``` +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +``` + +### MIT License + +Used by: [adler2 2.0.1](https://github.com/oyvindln/adler2), [anyhow 1.0.104](https://github.com/dtolnay/anyhow), [async-channel 2.5.0](https://github.com/smol-rs/async-channel), [async-executor 1.14.0](https://github.com/smol-rs/async-executor), [async-io 2.6.0](https://github.com/smol-rs/async-io), [async-lock 3.4.2](https://github.com/smol-rs/async-lock), [async-process 2.5.0](https://github.com/smol-rs/async-process), [async-signal 0.2.14](https://github.com/smol-rs/async-signal), [async-task 4.7.1](https://github.com/smol-rs/async-task), [async-trait 0.1.92](https://github.com/dtolnay/async-trait), [atomic-waker 1.1.2](https://github.com/smol-rs/atomic-waker), [blocking 1.7.0](https://github.com/smol-rs/blocking), [concurrent-queue 2.5.0](https://github.com/smol-rs/concurrent-queue), [curve25519-dalek-derive 0.1.1](https://github.com/dalek-cryptography/curve25519-dalek), [displaydoc 0.2.7](https://github.com/yaahc/displaydoc), [endi 1.1.1](https://github.com/zeenix/endi), [event-listener-strategy 0.5.4](https://github.com/smol-rs/event-listener-strategy), [event-listener 5.4.2](https://github.com/smol-rs/event-listener), [fastrand 2.5.0](https://github.com/smol-rs/fastrand), [futures-lite 2.6.1](https://github.com/smol-rs/futures-lite), [itoa 1.0.18](https://github.com/dtolnay/itoa), [khronos-egl 6.0.0](https://github.com/timothee-haudebourg/khronos-egl), [lazy_static 1.5.1](https://github.com/rust-lang-nursery/lazy-static.rs), [linux-raw-sys 0.12.1](https://github.com/sunfishcode/linux-raw-sys), [linux-raw-sys 0.4.15](https://github.com/sunfishcode/linux-raw-sys), [num_enum 0.7.6](https://github.com/illicitonion/num_enum), [num_enum_derive 0.7.6](https://github.com/illicitonion/num_enum), [once_cell 1.21.4](https://github.com/matklad/once_cell), [ordered-stream 0.2.0](https://github.com/danieldg/ordered-stream), [parking 2.2.1](https://github.com/smol-rs/parking), [pastey 0.2.3](https://github.com/as1100k/pastey), [pin-project-lite 0.2.17](https://github.com/taiki-e/pin-project-lite), [piper 0.2.5](https://github.com/smol-rs/piper), [polling 3.11.0](https://github.com/smol-rs/polling), [proc-macro-crate 3.5.0](https://github.com/bkchr/proc-macro-crate), [proc-macro2 1.0.107](https://github.com/dtolnay/proc-macro2), [quote 1.0.47](https://github.com/dtolnay/quote), [rustc-hash 1.1.0](https://github.com/rust-lang-nursery/rustc-hash), [rustix 0.38.44](https://github.com/bytecodealliance/rustix), [rustix 1.1.5](https://github.com/bytecodealliance/rustix), [rustversion 1.0.23](https://github.com/dtolnay/rustversion), [serde 1.0.229](https://github.com/serde-rs/serde), [serde_core 1.0.229](https://github.com/serde-rs/serde), [serde_derive 1.0.229](https://github.com/serde-rs/serde), [serde_json 1.0.154](https://github.com/serde-rs/json), [serde_repr 0.1.21](https://github.com/dtolnay/serde-repr), [simd_cesu8 1.2.0](https://github.com/seancroach/simd_cesu8), [smol_str 0.2.2](https://github.com/rust-analyzer/smol_str), [syn 2.0.119](https://github.com/dtolnay/syn), [syn 3.0.9](https://github.com/dtolnay/syn), [thiserror-impl 1.0.69](https://github.com/dtolnay/thiserror), [thiserror-impl 2.0.21](https://github.com/dtolnay/thiserror), [thiserror 1.0.69](https://github.com/dtolnay/thiserror), [thiserror 2.0.21](https://github.com/dtolnay/thiserror), [unicode-ident 1.0.27](https://github.com/dtolnay/unicode-ident), [x11-dl 2.21.0](https://github.com/AltF02/x11-rs.git), [zmij 1.0.23](https://github.com/dtolnay/zmij), [zvariant_utils 4.2.0](https://github.com/z-galaxy/zbus/) + +``` +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [async-recursion 1.2.0](https://github.com/dcchut/async-recursion), [glow 0.17.0](https://github.com/grovesNL/glow) + +``` +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +``` + +### MIT License + +Used by: [allocator-api2 0.2.21](https://github.com/zakarumych/allocator-api2) + +``` +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [winnow 1.0.4](https://github.com/winnow-rs/winnow) + +``` +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [raw-window-metal 1.1.0](https://github.com/rust-windowing/raw-window-metal) + +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [android_system_properties 0.1.6](https://github.com/nical/android_system_properties) + +``` +The MIT License (MIT) + +Copyright (c) 2013 Nicolas Silva + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [spin 0.9.9](https://github.com/mvdnes/spin-rs.git) + +``` +The MIT License (MIT) + +Copyright (c) 2014 Mathijs van de Nes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +### MIT License + +Used by: [tracing-core 0.1.36](https://github.com/tokio-rs/tracing) + +``` +The MIT License (MIT) + +Copyright (c) 2014 Mathijs van de Nes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [typenum 1.20.1](https://github.com/paholg/typenum) + +``` +The MIT License (MIT) + +Copyright (c) 2014 Paho Lurie-Gregg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [byteorder-lite 0.1.0](https://github.com/image-rs/byteorder-lite), [byteorder 1.5.0](https://github.com/BurntSushi/byteorder), [memchr 2.8.3](https://github.com/BurntSushi/memchr) + +``` +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [strsim 0.11.1](https://github.com/rapidfuzz/strsim-rs) + +``` +The MIT License (MIT) + +Copyright (c) 2015 Danny Guo +Copyright (c) 2016 Titus Wormer +Copyright (c) 2018 Akash Kurdekar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [combine 4.6.9](https://github.com/Marwes/combine) + +``` +The MIT License (MIT) + +Copyright (c) 2015 Markus Westerlind + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +``` + +### MIT License + +Used by: [webbrowser 1.2.4](https://github.com/amodm/webbrowser-rs) + +``` +The MIT License (MIT) + +Copyright (c) 2015-2022 Amod Malviya + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [jni 0.21.1](https://github.com/jni-rs/jni-rs) + +``` +The MIT License (MIT) + +Copyright (c) 2016 Prevoty, Inc. and jni-rs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [guillotiere 0.7.0](https://github.com/nical/guillotiere) + +``` +The MIT License (MIT) + +Copyright (c) 2019 Nicolas Silva + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [crossbeam-channel 0.5.17](https://github.com/crossbeam-rs/crossbeam), [crossbeam-deque 0.8.8](https://github.com/crossbeam-rs/crossbeam), [crossbeam-epoch 0.9.21](https://github.com/crossbeam-rs/crossbeam), [crossbeam-queue 0.3.14](https://github.com/crossbeam-rs/crossbeam), [crossbeam-utils 0.8.23](https://github.com/crossbeam-rs/crossbeam) + +``` +The MIT License (MIT) + +Copyright (c) 2019 The Crossbeam Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +### MIT License + +Used by: [async-broadcast 0.7.2](https://github.com/smol-rs/async-broadcast) + +``` +The MIT License (MIT) + +Copyright (c) 2020 Yoshua Wuyts + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [xkbcommon-dl 0.4.2](https://github.com/rust-windowing/xkbcommon-dl) + +``` +The MIT License (MIT) + +Copyright (c) 2023 Kirill Chibisov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [harfrust 0.12.0](https://github.com/harfbuzz/harfrust) + +``` +The MIT License (MIT) + +Copyright (c) HarfBuzz developers +Copyright (c) 2020 Yevhenii Reizner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [weezl 0.1.12](https://github.com/image-rs/weezl) + +``` +The MIT License (MIT) + +Copyright (c) HeroicKatora 2020 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +``` + +### MIT License + +Used by: [os_pipe 1.2.3](https://github.com/oconnor663/os_pipe.rs) + +``` +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### MIT License + +Used by: [nu-ansi-term 0.50.3](https://github.com/nushell/nu-ansi-term) + +``` +The MIT License (MIT) + +Copyright (c) 2014 Benjamin Sago +Copyright (c) 2021-2022 The Nushell Project Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +### MIT License + +Used by: [generic-array 0.14.7](https://github.com/fizyk20/generic-array.git) + +``` +The MIT License (MIT) + +Copyright (c) 2015 Bartłomiej Kamiński + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +### MIT License + +Used by: [quick-xml 0.41.0](https://github.com/tafia/quick-xml) + +``` +The MIT License (MIT) + +Copyright (c) 2016 Johann Tuffe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### Mozilla Public License 2.0 + +Used by: [option-ext 0.2.0](https://github.com/soc/option-ext.git) + +``` +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +``` + +### SIL Open Font License 1.1 + +Used by: [epaint_default_fonts 0.37.0](https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts) + +``` +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +``` + +### Ubuntu Font Licence v1.0 + +Used by: [epaint_default_fonts 0.37.0](https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts) + +``` +------------------------------- +UBUNTU FONT LICENCE Version 1.0 +------------------------------- + +PREAMBLE +This licence allows the licensed fonts to be used, studied, modified and +redistributed freely. The fonts, including any derivative works, can be +bundled, embedded, and redistributed provided the terms of this licence +are met. The fonts and derivatives, however, cannot be released under +any other licence. The requirement for fonts to remain under this +licence does not require any document created using the fonts or their +derivatives to be published under this licence, as long as the primary +purpose of the document is not to be a vehicle for the distribution of +the fonts. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this licence and clearly marked as such. This may +include source files, build scripts and documentation. + +"Original Version" refers to the collection of Font Software components +as received under this licence. + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to +a new environment. + +"Copyright Holder(s)" refers to all individuals and companies who have a +copyright ownership of the Font Software. + +"Substantially Changed" refers to Modified Versions which can be easily +identified as dissimilar to the Font Software by users of the Font +Software comparing the Original Version with the Modified Version. + +To "Propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification and with or without charging +a redistribution fee), making available to the public, and in some +countries other activities as well. + +PERMISSION & CONDITIONS +This licence does not grant any rights under trademark law and all such +rights are reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to propagate the Font Software, subject to +the below conditions: + +1) Each copy of the Font Software must contain the above copyright +notice and this licence. These can be included either as stand-alone +text files, human-readable headers or in the appropriate machine- +readable metadata fields within text or binary files as long as those +fields can be easily viewed by the user. + +2) The font name complies with the following: +(a) The Original Version must retain its name, unmodified. +(b) Modified Versions which are Substantially Changed must be renamed to +avoid use of the name of the Original Version or similar names entirely. +(c) Modified Versions which are not Substantially Changed must be +renamed to both (i) retain the name of the Original Version and (ii) add +additional naming elements to distinguish the Modified Version from the +Original Version. The name of such Modified Versions must be the name of +the Original Version, with "derivative X" where X represents the name of +the new work, appended to that name. + +3) The name(s) of the Copyright Holder(s) and any contributor to the +Font Software shall not be used to promote, endorse or advertise any +Modified Version, except (i) as required by this licence, (ii) to +acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with +their explicit written permission. + +4) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this licence, and must not be distributed +under any other licence. The requirement for fonts to remain under this +licence does not affect any document created using the Font Software, +except any version of the Font Software extracted from a document +created using the Font Software may only be distributed under this +licence. + +TERMINATION +This licence becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. + +``` + +### Unicode License v3 + +Used by: [unicode-ident 1.0.27](https://github.com/dtolnay/unicode-ident) + +``` +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2023 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +``` + +### Unicode License v3 + +Used by: [icu_collections 2.3.0](https://github.com/unicode-org/icu4x), [icu_locale_core 2.3.0](https://github.com/unicode-org/icu4x), [icu_normalizer 2.3.0](https://github.com/unicode-org/icu4x), [icu_normalizer_data 2.3.0](https://github.com/unicode-org/icu4x), [icu_properties 2.3.0](https://github.com/unicode-org/icu4x), [icu_properties_data 2.3.0](https://github.com/unicode-org/icu4x), [icu_provider 2.3.1](https://github.com/unicode-org/icu4x), [litemap 0.8.3](https://github.com/unicode-org/icu4x), [potential_utf 0.1.6](https://github.com/unicode-org/icu4x), [tinystr 0.8.4](https://github.com/unicode-org/icu4x), [writeable 0.6.4](https://github.com/unicode-org/icu4x), [yoke-derive 0.8.4](https://github.com/unicode-org/icu4x), [yoke 0.8.3](https://github.com/unicode-org/icu4x), [zerofrom-derive 0.1.8](https://github.com/unicode-org/icu4x), [zerofrom 0.1.8](https://github.com/unicode-org/icu4x), [zerotrie 0.2.5](https://github.com/unicode-org/icu4x), [zerovec-derive 0.11.6](https://github.com/unicode-org/icu4x), [zerovec 0.11.8](https://github.com/unicode-org/icu4x) + +``` +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. + +``` + +### The Unlicense + +Used by: [ksni 0.3.7](https://github.com/iovxw/ksni) + +``` +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +``` + +### Do What The F*ck You Want To Public License + +Used by: [ffmpeg-sys-next 9.0.1](https://github.com/zmwangx/rust-ffmpeg-sys) + +``` +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + +``` + +### zlib License + +Used by: [foldhash 0.1.5](https://github.com/orlp/foldhash), [foldhash 0.2.0](https://github.com/orlp/foldhash) + +``` +Copyright (c) 2024 Orson Peters + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. +``` + diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md new file mode 100644 index 0000000..36c313b --- /dev/null +++ b/THIRD-PARTY-NOTICES.md @@ -0,0 +1,91 @@ +# Third-party software in Silkscreen + +Silkscreen itself is licensed under the MIT License or the Apache License 2.0, +at your option ([LICENSE-MIT](LICENSE-MIT), [LICENSE-APACHE](LICENSE-APACHE)). +It builds on the work below; thank you to everyone who made it. + +The Rust crates compiled into Silkscreen, with their full license texts, are +listed in [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md). + +## FFmpeg + +Video encoding and decoding on Windows, macOS and Linux. +FFmpeg is a trademark of Fabrice Bellard. Silkscreen uses FFmpeg's libraries +unmodified, as separate shared libraries. + +- **Windows and Linux downloads** include FFmpeg 8.1 shared libraries + (`avcodec`, `avutil`, `swresample`) from + [BtbN/FFmpeg-Builds](https://github.com/BtbN/FFmpeg-Builds) (the `lgpl-shared` + builds), licensed under the **GNU Lesser General Public License v3**; the + license is included in each download as `FFMPEG-LICENSE.txt`. You may replace + these libraries with your own build of the same FFmpeg version. Source code: + (tag `n8.1`) and the build scripts at + BtbN/FFmpeg-Builds. +- **The macOS app** includes Homebrew's FFmpeg build + (), which enables GPL components + such as x264 and x265 and is licensed under the **GNU General Public License + v3 or later**. That copy of Silkscreen.app is therefore distributed under the + GPL v3 as a whole; Silkscreen's own MIT/Apache-2.0 code is compatible with + it, and all of its source code is in this repository. FFmpeg's source code: + ; the formula lists the exact version and + build options. `silkscreen doctor` prints the FFmpeg version in use. +- **The Android app** does not include FFmpeg; it decodes with Android's + MediaCodec. + +## PyroWave + +The experimental PyroWave codec. +Copyright (c) Hans-Kristian Arntzen. **MIT License** (included in the +downloads as `PYROWAVE-LICENSE.txt`). The macOS app includes PyroWave's Metal +port; the Windows and Linux downloads include `libpyrowave-shared`, built from +PyroWave's Vulkan implementation together with the parts below. + +- **Granite** — , Copyright (c) + 2017-2026 Hans-Kristian Arntzen, **MIT License**. +- **volk** — , Copyright (c) 2018-2026 Arseny + Kapoulkine, **MIT License**. +- **Vulkan-Headers** — , + Copyright The Khronos Group Inc., **Apache License 2.0 or MIT License**. + +The MIT License text (with each project's copyright line as above): + +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## Android app libraries + +The Android app uses AndroidX (core-ktx, activity-compose) and Jetpack Compose +(UI, Foundation, Material 3), Copyright The Android Open Source Project, +**Apache License 2.0** (see [LICENSE-APACHE](LICENSE-APACHE)). + + +## Parsec Virtual Display Driver + +On Windows, Silkscreen creates virtual monitors through the Parsec Virtual +Display Driver, which is installed separately and not included with +Silkscreen. Silkscreen's code that talks to the driver follows the interface +documented by (BSD 3-Clause License). + +## Fonts + +The app's interface uses the fonts bundled with egui (`epaint_default_fonts`): +Ubuntu Light (Ubuntu Font Licence 1.0), Hack (MIT), Noto Emoji (SIL Open Font +License 1.1), emoji-icon-font (MIT) and egui's icon font. Their licenses are in +[THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md). diff --git a/about.hbs b/about.hbs new file mode 100644 index 0000000..0947fc2 --- /dev/null +++ b/about.hbs @@ -0,0 +1,28 @@ +# Third-party Rust crates in Silkscreen + +Generated by [cargo-about](https://github.com/EmbarkStudios/cargo-about) from +the crates built into Silkscreen on Windows, macOS, Linux and Android +(regenerate with `scripts/third-party-licenses.sh`). Other bundled software +(FFmpeg, PyroWave and the parts of Granite it uses) is listed in +[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md). + +## Summary + +| License | Crates | +|---|---| +{{#each overview}} +| {{{name}}} | {{count}} | +{{/each}} + +## License texts + +{{#each licenses}} +### {{{name}}} + +Used by: {{#each used_by}}{{#if @first}}{{else}}, {{/if}}[{{crate.name}} {{crate.version}}]({{#if crate.repository}}{{crate.repository}}{{else}}https://crates.io/crates/{{crate.name}}{{/if}}){{/each}} + +``` +{{{text}}} +``` + +{{/each}} diff --git a/about.toml b/about.toml new file mode 100644 index 0000000..2faf1a3 --- /dev/null +++ b/about.toml @@ -0,0 +1,30 @@ +# cargo-about: lists every Rust crate built into Silkscreen and its license, +# for THIRD-PARTY-LICENSES.md (regenerate with scripts/third-party-licenses.sh). +accepted = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "Zlib", + "Unicode-3.0", + "Unlicense", + "CC0-1.0", + "MPL-2.0", + "BSL-1.0", + "CDLA-Permissive-2.0", + "OFL-1.1", + "Ubuntu-font-1.0", + "LicenseRef-UFL-1.0", + "WTFPL", +] +targets = [ + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "aarch64-apple-darwin", + "aarch64-linux-android", +] +ignore-build-dependencies = true +ignore-dev-dependencies = true diff --git a/scripts/third-party-licenses.sh b/scripts/third-party-licenses.sh new file mode 100755 index 0000000..b665824 --- /dev/null +++ b/scripts/third-party-licenses.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Regenerates THIRD-PARTY-LICENSES.md (the Rust crates' licenses) with +# cargo-about: `cargo install cargo-about --locked --features cli`. +set -euo pipefail +cd "$(dirname "$0")/.." +cargo about generate about.hbs -o THIRD-PARTY-LICENSES.md +echo "wrote THIRD-PARTY-LICENSES.md" From 6909a59a5694dda452012199952d6b807b2579b9 Mon Sep 17 00:00:00 2001 From: BenTech2 <5576214+BenTech2@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:01:48 -0400 Subject: [PATCH 3/3] PyroWave build: give mklink plain Windows paths cmd read the '/' in a mixed-slash path with '..' as a switch, so linking Granite into PyroWave failed on the Windows CI runner. Co-Authored-By: Claude Opus 5.5 --- crates/silkscreen-codec/build.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/silkscreen-codec/build.rs b/crates/silkscreen-codec/build.rs index d9eceb1..5db8597 100644 --- a/crates/silkscreen-codec/build.rs +++ b/crates/silkscreen-codec/build.rs @@ -111,12 +111,24 @@ mod pyrowave { /// A directory junction: unlike a symlink, needs no special rights. #[cfg(windows)] fn link_dir(target: &Path, link: &Path) { + // cmd takes a `/` for the start of a switch, so pass plain absolute + // paths with backslashes (no `..`, no `\\?\` prefix). + fn plain(p: &Path) -> String { + let p = p.canonicalize().expect("resolving a path for mklink"); + let s = p.display().to_string(); + s.strip_prefix(r"\\?\").unwrap_or(&s).to_owned() + } + let link = format!( + r"{}\{}", + plain(link.parent().unwrap()), + link.file_name().unwrap().to_string_lossy() + ); let ok = std::process::Command::new("cmd") .args(["/C", "mklink", "/J"]) - .arg(link) - .arg(target) + .arg(&link) + .arg(plain(target)) .status() .is_ok_and(|s| s.success()); - assert!(ok, "linking Granite into PyroWave"); + assert!(ok, "linking Granite into PyroWave ({link})"); } }