diff --git a/.github/workflows/before_deploy.sh b/.github/workflows/before_deploy.sh new file mode 100644 index 0000000..c48096f --- /dev/null +++ b/.github/workflows/before_deploy.sh @@ -0,0 +1,29 @@ +set -ex + +main() { + local tag=$(git tag --points-at HEAD) + local src=$(pwd) \ + stage= + + if [ "$OS_NAME" = "macOS-latest" ]; then + stage=$(mktemp -d -t tmp) + else + stage=$(mktemp -d) + fi + + cp target/$TARGET/release/obs_livesplit_one.dll $stage/obs-livesplit-one.dll 2>/dev/null || : + cp target/$TARGET/release/libobs_livesplit_one.so $stage/libobs_livesplit_one.so 2>/dev/null || : + cp target/$TARGET/release/libobs_livesplit_one.dylib $stage/libobs_livesplit_one.dylib 2>/dev/null || : + + cd $stage + if [ "$OS_NAME" = "windows-latest" ]; then + 7z a $src/obs-livesplit-one-$tag-$TARGET.zip * + else + tar czf $src/obs-livesplit-one-$tag-$TARGET.tar.gz * + fi + cd $src + + rm -rf $stage +} + +main diff --git a/.github/workflows/build_shared.sh b/.github/workflows/build_shared.sh new file mode 100644 index 0000000..b7d0a4d --- /dev/null +++ b/.github/workflows/build_shared.sh @@ -0,0 +1,16 @@ +set -ex + +main() { + local cargo=cross + if [ "$SKIP_CROSS" = "skip" ]; then + cargo=cargo + fi + local release_flag="" + if [ "$IS_DEPLOY" = "true" ]; then + release_flag="--release" + fi + + $cargo build --target $TARGET $release_flag $FEATURES +} + +main diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..bb46dbe --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,111 @@ +name: Rust + +on: + pull_request: + push: + branches: + - 'master' + tags: + - '*' + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + label: + - Windows x86_64 + # - Linux x86_64 + # - macOS x86_64 + + include: + - label: Windows x86_64 + target: x86_64-pc-windows-msvc + os: windows-latest + cross: skip + + # - label: Linux x86_64 + # target: x86_64-unknown-linux-gnu + # os: ubuntu-latest + # cross: skip + + # - label: macOS x86_64 + # target: x86_64-apple-darwin + # os: macOS-latest + # cross: skip + + steps: + - name: Checkout Commit + uses: actions/checkout@v2 + + - name: Install Rust + uses: hecrj/setup-rust-action@v1 + with: + rust-version: ${{ matrix.toolchain || 'stable' }} + + - name: Install Target + if: matrix.install_target != '' + run: rustup target add ${{ matrix.target }} + + - name: Install cross + if: matrix.cross == '' + run: sh .github/workflows/install.sh + env: + OS_NAME: ${{ matrix.os }} + + - name: Build Shared Library + if: matrix.dylib == '' + run: sh .github/workflows/build_shared.sh + env: + TARGET: ${{ matrix.target }} + SKIP_CROSS: ${{ matrix.cross }} + IS_DEPLOY: ${{ startsWith(github.ref, 'refs/tags/') && (matrix.release_anyway != '' || !(startsWith(matrix.toolchain, 'nightly') || startsWith(matrix.toolchain, 'beta'))) }} + FEATURES: ${{ matrix.features }} + + - name: Prepare Release + if: startsWith(github.ref, 'refs/tags/') && matrix.release == '' + shell: bash + run: .github/workflows/before_deploy.sh + env: + OS_NAME: ${{ matrix.os }} + TARGET: ${{ matrix.target }} + + - name: Release + if: startsWith(github.ref, 'refs/tags/') && matrix.release == '' + uses: softprops/action-gh-release@v1 + with: + files: obs-livesplit-one-*.* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + clippy: + runs-on: ubuntu-latest + steps: + - name: Checkout Commit + uses: actions/checkout@v2 + + - name: Install Rust + uses: hecrj/setup-rust-action@v1 + with: + components: clippy + + - name: Run Clippy + run: cargo clippy --all-features + + format: + runs-on: ubuntu-latest + steps: + - name: Checkout Commit + uses: actions/checkout@v2 + + - name: Install Rust + uses: hecrj/setup-rust-action@v1 + with: + components: rustfmt + + - name: Run cargo fmt + run: cargo fmt -- --check || true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e3283b9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1354 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + +[[package]] +name = "approx" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "bytemuck" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "cc" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "winapi 0.3.9", +] + +[[package]] +name = "cmake" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +dependencies = [ + "cc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "core-graphics" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "core-text" +version = "19.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c7f46e8b820fd5f4b28528104b28b0a91cbe9e9c5bde8017087fb44bc93a60" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if 0.1.10", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi 0.3.9", + "wio", +] + +[[package]] +name = "euclid" +version = "0.20.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb7ef65b3777a325d1eeefefab5b6d4959da54747e33bd6258e789640f307ad" +dependencies = [ + "num-traits", +] + +[[package]] +name = "euclid" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5337024b8293bdce5265dc9570ef6e608a34bfacbbc87fe1a5dcb5f1dac2f4e2" +dependencies = [ + "num-traits", +] + +[[package]] +name = "expat-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +dependencies = [ + "cmake", + "pkg-config", +] + +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "font-kit" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9042cb45150fb2b2a012fc03d0f1d2071f18e90397b9d2a5ec8ade8464bf20" +dependencies = [ + "bitflags", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "servo-fontconfig", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "image" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce04077ead78e39ae8610ad26216aed811996b043d47beed5090db674f9e9b5" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "jpeg-decoder" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc797adac5f083b8ff0ca6f6294a999393d76e197c36488e2ef732c4715f6fa3" +dependencies = [ + "byteorder", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" + +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + +[[package]] +name = "livesplit-core" +version = "0.11.0" +source = "git+https://github.com/CryZe/livesplit-core?branch=path-renderer#03eaada71a5f3a580c13db8cca6cce67a0580f16" +dependencies = [ + "base64", + "bytemuck", + "byteorder", + "cfg-if 1.0.0", + "chrono", + "derive_more", + "euclid 0.20.14", + "font-kit", + "hashbrown", + "image", + "indexmap", + "libm", + "livesplit-hotkey", + "livesplit-title-abbreviations", + "lyon", + "odds", + "ordered-float", + "palette", + "parking_lot", + "quick-xml", + "rustybuzz", + "serde", + "serde_json", + "smallstr", + "snafu", + "tiny-skia", + "ttf-parser", + "unicase", + "utf-8", +] + +[[package]] +name = "livesplit-hotkey" +version = "0.5.0" +source = "git+https://github.com/CryZe/livesplit-core?branch=path-renderer#03eaada71a5f3a580c13db8cca6cce67a0580f16" +dependencies = [ + "cfg-if 1.0.0", + "mio", + "parking_lot", + "promising-future", + "serde", + "snafu", + "winapi 0.3.9", + "x11-dl", +] + +[[package]] +name = "livesplit-title-abbreviations" +version = "0.1.0" +source = "git+https://github.com/CryZe/livesplit-core?branch=path-renderer#03eaada71a5f3a580c13db8cca6cce67a0580f16" +dependencies = [ + "unicase", +] + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "lyon" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d083e12e9e22298eec27751ec4a0975abac5873a3b0dcdbbecc608d333f0e9" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11033936a5e9d7bf35b7ba71a19f8b6bc838f3206304175dc5b3524ca16672a8" +dependencies = [ + "lyon_path", + "sid", +] + +[[package]] +name = "lyon_geom" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce4e12203c428a58200b8cf1c0a3aad1cda907008ea11310bb3729593e5f933" +dependencies = [ + "arrayvec", + "euclid 0.22.1", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243c71fff16677ecde2d38794a0253a8f0417cebaacc6390677c30733831d8cb" +dependencies = [ + "lyon_geom", +] + +[[package]] +name = "lyon_tessellation" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ecf3d769bec66396957d7c5cb91f998c4182e53fdc96cc435b6ebcd46a63cd9" +dependencies = [ + "arrayvec", + "lyon_path", + "sid", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "obs-livesplit-one" +version = "0.1.0" +dependencies = [ + "livesplit-core", + "once_cell", +] + +[[package]] +name = "odds" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe9f693ec5bf79b8867b34aeaa5a2500e3a90461b486b573bcad3cca42b4d9e" +dependencies = [ + "rawpointer", + "rawslice", + "unchecked-index", +] + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "ordered-float" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dacdec97876ef3ede8c50efc429220641a0b11ba0048b4b0c357bccbc47c5204" +dependencies = [ + "num-traits", +] + +[[package]] +name = "palette" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a05c0334468e62a4dfbda34b29110aa7d70d58c7fdb2c9857b5874dd9827cc59" +dependencies = [ + "approx", + "num-traits", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", +] + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b451513912d6b3440e443aa75a73ab22203afedc4a90df8526d008c0f86f7cb3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "promising-future" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44ba461c1b8785e502867026d893fa52801faccfbfe59efdae7da4b9094b4ce2" +dependencies = [ + "threadpool", +] + +[[package]] +name = "quick-xml" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rawslice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e23c908b26a742e5e3768ea42f19225ef809d3c9e3071bfe3e01c7e9b6fd1cd" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustybuzz" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10" +dependencies = [ + "bitflags", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-general-category", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "servo-fontconfig" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +dependencies = [ + "libc", + "servo-fontconfig-sys", +] + +[[package]] +name = "servo-fontconfig-sys" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +dependencies = [ + "expat-sys", + "freetype-sys", + "pkg-config", +] + +[[package]] +name = "sid" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5ac56c121948b4879bba9e519852c211bcdd8f014efff766441deff0b91bdb" +dependencies = [ + "num-traits", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallstr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e922794d168678729ffc7e07182721a14219c65814e66e91b839a272fe5ae4f" +dependencies = [ + "smallvec", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "snafu" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.3", + "weezl", +] + +[[package]] +name = "tiny-skia" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cd9212186c74db2a470e6f264c0da72e9094979cd701a167c23d34f3ff3ebd" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "png", + "wide", +] + +[[package]] +name = "ttf-parser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca" + +[[package]] +name = "unchecked-index" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4a1771ba99c4c8f6e5b1a37a208e970e77ed7297e29963dd1a2303601cd33" + +[[package]] +name = "unicode-general-category" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" + +[[package]] +name = "unicode-script" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bf4d5fc96546fdb73f9827097810bbda93b11a6770ff3a54e1f445d4135787" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "weezl" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2bb9fc8309084dd7cd651336673844c1d47f8ef6d2091ec160b27f5c4aa277" + +[[package]] +name = "wide" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c51f160ef6333906fad7d31a2010d449148dd1a26bc377e0a1ace2cc9e1e1ee" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "x11-dl" +version = "2.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" +dependencies = [ + "lazy_static", + "libc", + "maybe-uninit", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2e06b0d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "obs-livesplit-one" +version = "0.1.0" +authors = ["Christopher Serr "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +livesplit-core = { git = "https://github.com/CryZe/livesplit-core", branch = "path-renderer", features = ["software-rendering", "font-loading"] } +once_cell = "1.5.2" + +[profile.dev.package."*"] +opt-level = 3 +debug = false +debug-assertions = false +overflow-checks = false + +[profile.dev.build-override] +opt-level = 0 + +[profile.release] +lto = true +panic = "abort" + +[profile.release.build-override] +opt-level = 0 diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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..15c00b5 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Christopher Serr, Sergey Papushin, Cris Hall-Ramos + +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 new file mode 100644 index 0000000..2c9c66b --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# LiveSplit obs-livesplit-one + +A plugin for OBS Studio that allows adding LiveSplit One as a source. + +## Notes +```sh +lib /def:exports.def /OUT:obs.lib /MACHINE:x64 +``` +in msvc console diff --git a/exports.def b/exports.def new file mode 100644 index 0000000..c6b4263 --- /dev/null +++ b/exports.def @@ -0,0 +1,27 @@ +LIBRARY obs +EXPORTS +obs_register_source_s +gs_texture_create +obs_source_draw +obs_enter_graphics +obs_leave_graphics +gs_texture_set_image +obs_hotkey_register_source +obs_properties_create +obs_properties_add_path +obs_data_get_string +blog +obs_properties_add_int +obs_data_get_int +gs_texture_destroy +gs_draw_sprite +gs_effect_get_param_by_name +gs_effect_get_technique +gs_effect_set_texture +gs_technique_begin +gs_technique_begin_pass +gs_technique_end +gs_technique_end_pass +obs_get_base_effect +obs_data_set_default_int +obs_properties_add_button diff --git a/obs.exp b/obs.exp new file mode 100644 index 0000000..25676d8 Binary files /dev/null and b/obs.exp differ diff --git a/obs.lib b/obs.lib new file mode 100644 index 0000000..a6cea25 Binary files /dev/null and b/obs.lib differ diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 0000000..159b582 --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,376 @@ +// Based on https://docs.rs/crate/obs-sys/0.1.3/source/generated/bindings.rs +// There's a few changes: +// - enums used to be prefixed by the type. But they already are anyway. +// So obs_icon_type_OBS_ICON_TYPE_GAME_CAPTURE is just OBS_ICON_TYPE_GAME_CAPTURE +// - The functions properly link against libobs. +// - No dependency on bindgen, which just causes trouble. +// - size_t is usize, not ulong, which would be u32 on 64-bit Windows. + +#![allow(non_camel_case_types)] + +use std::{ + ffi::c_void, + os::raw::{c_char, c_int, c_long, c_longlong}, +}; + +pub type gs_color_format = u32; +pub const GS_RGBA: gs_color_format = 3; +pub type gs_effect_t = gs_effect; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gs_effect { + _unused: [u8; 0], +} + +pub type gs_eparam_t = gs_effect_param; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gs_effect_param { + _unused: [u8; 0], +} + +pub type gs_technique_t = gs_effect_technique; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gs_effect_technique { + _unused: [u8; 0], +} + +pub type gs_texture_t = gs_texture; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gs_texture { + _unused: [u8; 0], +} + +pub type obs_base_effect = u32; +pub const OBS_EFFECT_PREMULTIPLIED_ALPHA: obs_base_effect = 7; + +pub type obs_data_t = obs_data; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_data { + _unused: [u8; 0], +} + +pub type obs_hotkey_func = Option< + unsafe extern "C" fn( + data: *mut c_void, + id: obs_hotkey_id, + hotkey: *mut obs_hotkey_t, + pressed: bool, + ), +>; + +pub type size_t = usize; +pub type obs_hotkey_id = size_t; + +pub type obs_hotkey_t = obs_hotkey; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_hotkey { + _unused: [u8; 0], +} + +pub const OBS_ICON_TYPE_GAME_CAPTURE: obs_icon_type = 8; +pub type obs_icon_type = u32; + +pub type obs_module_t = obs_module; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_module { + _unused: [u8; 0], +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct obs_mouse_event { + pub modifiers: u32, + pub x: i32, + pub y: i32, +} + +pub type obs_path_type = u32; +pub const OBS_PATH_FILE: obs_path_type = 0; + +pub type obs_properties_t = obs_properties; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_properties { + _unused: [u8; 0], +} + +pub type obs_property_clicked_t = Option< + unsafe extern "C" fn( + props: *mut obs_properties_t, + property: *mut obs_property_t, + data: *mut c_void, + ) -> bool, +>; + +pub type obs_property_t = obs_property; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_property { + _unused: [u8; 0], +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_source_info { + pub id: *const c_char, + pub type_: obs_source_type, + pub output_flags: u32, + pub get_name: Option *const c_char>, + pub create: Option< + unsafe extern "C" fn(settings: *mut obs_data_t, source: *mut obs_source_t) -> *mut c_void, + >, + pub destroy: Option, + pub get_width: Option u32>, + pub get_height: Option u32>, + pub get_defaults: Option, + pub get_properties: Option *mut obs_properties_t>, + pub update: Option, + pub activate: Option, + pub deactivate: Option, + pub show: Option, + pub hide: Option, + pub video_tick: Option, + pub video_render: Option, + pub filter_video: Option< + unsafe extern "C" fn( + data: *mut c_void, + frame: *mut obs_source_frame, + ) -> *mut obs_source_frame, + >, + pub filter_audio: Option< + unsafe extern "C" fn(data: *mut c_void, audio: *mut obs_audio_data) -> *mut obs_audio_data, + >, + pub enum_active_sources: Option< + unsafe extern "C" fn( + data: *mut c_void, + enum_callback: obs_source_enum_proc_t, + param: *mut c_void, + ), + >, + pub save: Option, + pub load: Option, + pub mouse_click: Option< + unsafe extern "C" fn( + data: *mut c_void, + event: *const obs_mouse_event, + type_: i32, + mouse_up: bool, + click_count: u32, + ), + >, + pub mouse_move: Option< + unsafe extern "C" fn(data: *mut c_void, event: *const obs_mouse_event, mouse_leave: bool), + >, + pub mouse_wheel: Option< + unsafe extern "C" fn( + data: *mut c_void, + event: *const obs_mouse_event, + x_delta: c_int, + y_delta: c_int, + ), + >, + pub focus: Option, + pub key_click: + Option, + pub filter_remove: Option, + pub type_data: *mut c_void, + pub free_type_data: Option, + pub audio_render: Option< + unsafe extern "C" fn( + data: *mut c_void, + ts_out: *mut u64, + audio_output: *mut obs_source_audio_mix, + mixers: u32, + channels: size_t, + sample_rate: size_t, + ) -> bool, + >, + pub enum_all_sources: Option< + unsafe extern "C" fn( + data: *mut c_void, + enum_callback: obs_source_enum_proc_t, + param: *mut c_void, + ), + >, + pub transition_start: Option, + pub transition_stop: Option, + pub get_defaults2: + Option, + pub get_properties2: Option< + unsafe extern "C" fn(data: *mut c_void, type_data: *mut c_void) -> *mut obs_properties_t, + >, + pub audio_mix: Option< + unsafe extern "C" fn( + data: *mut c_void, + ts_out: *mut u64, + audio_output: *mut audio_output_data, + channels: size_t, + sample_rate: size_t, + ) -> bool, + >, + pub icon_type: obs_icon_type, + pub media_play_pause: Option, + pub media_restart: Option, + pub media_stop: Option, + pub media_next: Option, + pub media_previous: Option, + pub media_get_duration: Option i64>, + pub media_get_time: Option i64>, + pub media_set_time: Option, + pub media_get_state: Option obs_media_state>, + pub version: u32, + pub unversioned_id: *const c_char, +} + +pub type obs_source_type = u32; +pub const OBS_SOURCE_TYPE_INPUT: obs_source_type = 0; + +pub type obs_source_t = obs_source; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_source { + _unused: [u8; 0], +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_source_frame { + pub data: [*mut u8; 8usize], + pub linesize: [u32; 8usize], + pub width: u32, + pub height: u32, + pub timestamp: u64, + pub format: video_format, + pub color_matrix: [f32; 16usize], + pub full_range: bool, + pub color_range_min: [f32; 3usize], + pub color_range_max: [f32; 3usize], + pub flip: bool, + pub refs: c_long, + pub prev_frame: bool, +} + +pub type video_format = u32; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_audio_data { + pub data: [*mut u8; 8usize], + pub frames: u32, + pub timestamp: u64, +} + +pub type obs_source_enum_proc_t = Option< + unsafe extern "C" fn(parent: *mut obs_source_t, child: *mut obs_source_t, param: *mut c_void), +>; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_key_event { + pub modifiers: u32, + pub text: *mut c_char, + pub native_modifiers: u32, + pub native_scancode: u32, + pub native_vkey: u32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct obs_source_audio_mix { + pub output: [audio_output_data; 6usize], +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct audio_output_data { + pub data: [*mut f32; 8usize], +} + +pub type obs_media_state = u32; + +pub const GS_DYNAMIC: u32 = 2; + +pub type _bindgen_ty_1 = u32; +pub const LOG_WARNING: _bindgen_ty_1 = 200; + +pub const OBS_SOURCE_CUSTOM_DRAW: u32 = 8; +pub const OBS_SOURCE_INTERACTION: u32 = 32; +pub const OBS_SOURCE_VIDEO: u32 = 1; + +#[link(name = "obs", kind = "dylib")] +extern "C" { + pub fn obs_register_source_s(info: *const obs_source_info, size: size_t); + pub fn gs_texture_create( + width: u32, + height: u32, + color_format: gs_color_format, + levels: u32, + data: *mut *const u8, + flags: u32, + ) -> *mut gs_texture_t; + pub fn obs_enter_graphics(); + pub fn obs_leave_graphics(); + pub fn gs_texture_set_image( + tex: *mut gs_texture_t, + data: *const u8, + linesize: u32, + invert: bool, + ); + pub fn obs_hotkey_register_source( + source: *mut obs_source_t, + name: *const c_char, + description: *const c_char, + func: obs_hotkey_func, + data: *mut c_void, + ) -> obs_hotkey_id; + pub fn obs_properties_create() -> *mut obs_properties_t; + pub fn obs_properties_add_path( + props: *mut obs_properties_t, + name: *const c_char, + description: *const c_char, + type_: obs_path_type, + filter: *const c_char, + default_path: *const c_char, + ) -> *mut obs_property_t; + pub fn obs_data_get_string(data: *mut obs_data_t, name: *const c_char) -> *const c_char; + pub fn blog(log_level: c_int, format: *const c_char, ...); + pub fn obs_properties_add_int( + props: *mut obs_properties_t, + name: *const c_char, + description: *const c_char, + min: c_int, + max: c_int, + step: c_int, + ) -> *mut obs_property_t; + pub fn obs_data_get_int(data: *mut obs_data_t, name: *const c_char) -> c_longlong; + pub fn gs_texture_destroy(tex: *mut gs_texture_t); + pub fn gs_draw_sprite(tex: *mut gs_texture_t, flip: u32, width: u32, height: u32); + pub fn gs_effect_get_param_by_name( + effect: *const gs_effect_t, + name: *const c_char, + ) -> *mut gs_eparam_t; + pub fn gs_effect_get_technique( + effect: *const gs_effect_t, + name: *const c_char, + ) -> *mut gs_technique_t; + pub fn gs_effect_set_texture(param: *mut gs_eparam_t, val: *mut gs_texture_t); + pub fn gs_technique_begin(technique: *mut gs_technique_t) -> size_t; + pub fn gs_technique_begin_pass(technique: *mut gs_technique_t, pass: size_t) -> bool; + pub fn gs_technique_end(technique: *mut gs_technique_t); + pub fn gs_technique_end_pass(technique: *mut gs_technique_t); + pub fn obs_get_base_effect(effect: obs_base_effect) -> *mut gs_effect_t; + pub fn obs_data_set_default_int(data: *mut obs_data_t, name: *const c_char, val: c_longlong); + pub fn obs_properties_add_button( + props: *mut obs_properties_t, + name: *const c_char, + text: *const c_char, + callback: obs_property_clicked_t, + ) -> *mut obs_property_t; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6c682b7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,536 @@ +use std::{ + cmp::Ordering, + ffi::{c_void, CStr}, + fmt, + fs::File, + io::{BufReader, BufWriter, Seek, SeekFrom}, + mem, + os::raw::{c_char, c_int}, + path::PathBuf, + ptr, +}; + +mod ffi; + +use ffi::{ + blog, gs_draw_sprite, gs_effect_get_param_by_name, gs_effect_get_technique, + gs_effect_set_texture, gs_effect_t, gs_technique_begin, gs_technique_begin_pass, + gs_technique_end, gs_technique_end_pass, gs_texture_create, gs_texture_destroy, + gs_texture_set_image, gs_texture_t, obs_data_get_int, obs_data_get_string, + obs_data_set_default_int, obs_data_t, obs_enter_graphics, obs_get_base_effect, obs_hotkey_id, + obs_hotkey_register_source, obs_hotkey_t, obs_leave_graphics, obs_module_t, obs_mouse_event, + obs_properties_add_button, obs_properties_add_int, obs_properties_add_path, + obs_properties_create, obs_properties_t, obs_property_t, obs_register_source_s, + obs_source_info, obs_source_t, GS_DYNAMIC, GS_RGBA, LOG_WARNING, + OBS_EFFECT_PREMULTIPLIED_ALPHA, OBS_ICON_TYPE_GAME_CAPTURE, OBS_PATH_FILE, + OBS_SOURCE_CUSTOM_DRAW, OBS_SOURCE_INTERACTION, OBS_SOURCE_TYPE_INPUT, OBS_SOURCE_VIDEO, +}; +use livesplit_core::{ + layout::{self, LayoutSettings, LayoutState}, + rendering::software::SoftwareRenderer, + run::{parser::composite, saver::livesplit::save_timer}, + Layout, Run, Segment, Timer, +}; +use once_cell::sync::Lazy; + +macro_rules! cstr { + ($f:literal) => { + concat!($f, '\0').as_ptr().cast() + }; +} + +static mut OBS_MODULE_POINTER: *mut obs_module_t = ptr::null_mut(); + +#[no_mangle] +pub extern "C" fn obs_module_set_pointer(module: *mut obs_module_t) { + unsafe { + OBS_MODULE_POINTER = module; + } +} + +#[no_mangle] +pub extern "C" fn obs_module_ver() -> u32 { + (26 << 24) | (1 << 16) | 1 +} + +struct UnsafeMultiThread(T); + +unsafe impl Sync for UnsafeMultiThread {} +unsafe impl Send for UnsafeMultiThread {} + +struct State { + timer: Timer, + layout: Layout, + state: LayoutState, + renderer: SoftwareRenderer, + texture: *mut gs_texture_t, + width: u32, + height: u32, +} + +struct Settings { + run: Run, + layout: Layout, + width: u32, + height: u32, +} + +fn parse_run(path: &CStr) -> Option { + let path = path.to_str().ok()?; + if path.is_empty() { + return None; + } + let reader = BufReader::new(File::open(path).ok()?); + let run = composite::parse(reader, Some(PathBuf::from(path)), true).ok()?; + if run.run.is_empty() { + return None; + } + Some(run.run) +} + +#[allow(unused)] +fn log(x: fmt::Arguments<'_>) { + let str = format!("{}\0", x); + unsafe { + blog(LOG_WARNING as _, str.as_ptr().cast()); + } +} + +fn parse_layout(path: &CStr) -> Option { + let path = path.to_str().ok()?; + if path.is_empty() { + return None; + } + let mut reader = BufReader::new(File::open(path).ok()?); + + if let Ok(settings) = LayoutSettings::from_json(&mut reader) { + return Some(Layout::from_settings(settings)); + } + + reader.seek(SeekFrom::Start(0)).ok()?; + layout::parser::parse(&mut reader).ok() +} + +unsafe fn parse_settings(settings: *mut obs_data_t) -> Settings { + let splits_path = CStr::from_ptr(obs_data_get_string(settings, SETTINGS_SPLITS_PATH).cast()); + let run = parse_run(splits_path).unwrap_or_else(default_run); + + let layout_path = CStr::from_ptr(obs_data_get_string(settings, SETTINGS_LAYOUT_PATH).cast()); + let layout = parse_layout(layout_path).unwrap_or_else(Layout::default_layout); + + let width = obs_data_get_int(settings, SETTINGS_WIDTH) as u32; + let height = obs_data_get_int(settings, SETTINGS_HEIGHT) as u32; + + Settings { + run, + layout, + width, + height, + } +} + +impl State { + unsafe fn new( + Settings { + run, + mut layout, + width, + height, + }: Settings, + ) -> Self { + let timer = Timer::new(run).unwrap(); + + let state = layout.state(&timer.snapshot()); + + let mut renderer = SoftwareRenderer::new(); + renderer.render(&state, [width, height]); + + let mut arr = [renderer.image().as_ptr(), ptr::null()]; + obs_enter_graphics(); + let texture = gs_texture_create(width, height, GS_RGBA, 1, arr.as_mut_ptr(), GS_DYNAMIC); + obs_leave_graphics(); + + Self { + timer, + layout, + state, + renderer, + texture, + width, + height, + } + } + + unsafe fn update(&mut self) { + self.layout + .update_state(&mut self.state, &self.timer.snapshot()); + self.renderer.render(&self.state, [self.width, self.height]); + gs_texture_set_image( + self.texture, + self.renderer.image().as_ptr(), + self.width * 4, + false, + ); + } +} + +unsafe extern "C" fn get_name(_: *mut c_void) -> *const c_char { + cstr!("LiveSplit One") +} + +unsafe extern "C" fn split( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.split_or_start(); + } +} + +unsafe extern "C" fn reset( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.reset(true); + } +} + +unsafe extern "C" fn undo( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.undo_split(); + } +} + +unsafe extern "C" fn skip( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.skip_split(); + } +} + +unsafe extern "C" fn pause( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.toggle_pause_or_start(); + } +} + +unsafe extern "C" fn undo_all_pauses( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.undo_all_pauses(); + } +} + +unsafe extern "C" fn previous_comparison( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.switch_to_previous_comparison(); + } +} + +unsafe extern "C" fn next_comparison( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.switch_to_next_comparison(); + } +} + +unsafe extern "C" fn toggle_timing_method( + data: *mut c_void, + _: obs_hotkey_id, + _: *mut obs_hotkey_t, + pressed: bool, +) { + if pressed { + let state: &mut State = &mut *data.cast(); + state.timer.toggle_timing_method(); + } +} + +unsafe extern "C" fn create(settings: *mut obs_data_t, source: *mut obs_source_t) -> *mut c_void { + let data = Box::into_raw(Box::new(State::new(parse_settings(settings)))).cast(); + + obs_hotkey_register_source( + source, + cstr!("hotkey_split"), + cstr!("Split"), + Some(split), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_reset"), + cstr!("Reset"), + Some(reset), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_undo"), + cstr!("Undo Split"), + Some(undo), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_skip"), + cstr!("Skip Split"), + Some(skip), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_pause"), + cstr!("Pause"), + Some(pause), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_undo_all_pauses"), + cstr!("Undo All Pauses"), + Some(undo_all_pauses), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_previous_comparison"), + cstr!("Previous Comparison"), + Some(previous_comparison), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_next_comparison"), + cstr!("Next Comparison"), + Some(next_comparison), + data, + ); + + obs_hotkey_register_source( + source, + cstr!("hotkey_toggle_timing_method"), + cstr!("Toggle Timing Method"), + Some(toggle_timing_method), + data, + ); + + data +} + +unsafe extern "C" fn destroy(data: *mut c_void) { + let state: Box = Box::from_raw(data.cast()); + obs_enter_graphics(); + gs_texture_destroy(state.texture); + obs_leave_graphics(); +} + +unsafe extern "C" fn get_width(data: *mut c_void) -> u32 { + let state: &mut State = &mut *data.cast(); + state.width +} + +unsafe extern "C" fn get_height(data: *mut c_void) -> u32 { + let state: &mut State = &mut *data.cast(); + state.height +} + +unsafe extern "C" fn video_render(data: *mut c_void, _: *mut gs_effect_t) { + let state: &mut State = &mut *data.cast(); + state.update(); + + let effect = obs_get_base_effect(OBS_EFFECT_PREMULTIPLIED_ALPHA); + let tech = gs_effect_get_technique(effect, cstr!("Draw")); + + gs_technique_begin(tech); + gs_technique_begin_pass(tech, 0); + + gs_effect_set_texture( + gs_effect_get_param_by_name(effect, cstr!("image")), + state.texture, + ); + gs_draw_sprite(state.texture, 0, 0, 0); + + gs_technique_end_pass(tech); + gs_technique_end(tech); +} + +unsafe extern "C" fn mouse_wheel( + data: *mut c_void, + _: *const obs_mouse_event, + _: c_int, + y_delta: c_int, +) { + let state: &mut State = &mut *data.cast(); + match y_delta.cmp(&0) { + Ordering::Less => state.layout.scroll_down(), + Ordering::Equal => {} + Ordering::Greater => state.layout.scroll_up(), + } +} + +unsafe extern "C" fn save_splits( + _: *mut obs_properties_t, + _: *mut obs_property_t, + data: *mut c_void, +) -> bool { + let state: &mut State = &mut *data.cast(); + if let Some(path) = state.timer.run().path() { + if let Ok(file) = File::create(path) { + let _ = save_timer(&state.timer, BufWriter::new(file)); + } + } + false +} + +const SETTINGS_WIDTH: *const c_char = cstr!("width"); +const SETTINGS_HEIGHT: *const c_char = cstr!("height"); +const SETTINGS_SPLITS_PATH: *const c_char = cstr!("splits_path"); +const SETTINGS_LAYOUT_PATH: *const c_char = cstr!("layout_path"); +const SETTINGS_SAVE_SPLITS: *const c_char = cstr!("save_splits"); + +unsafe extern "C" fn get_properties(_: *mut c_void) -> *mut obs_properties_t { + let props = obs_properties_create(); + obs_properties_add_int(props, SETTINGS_WIDTH, cstr!("Width"), 10, 8200, 10); + obs_properties_add_int(props, SETTINGS_HEIGHT, cstr!("Height"), 10, 8200, 10); + obs_properties_add_path( + props, + SETTINGS_SPLITS_PATH, + cstr!("Splits"), + OBS_PATH_FILE, + cstr!("LiveSplit Splits (*.lss)"), + ptr::null(), + ); + obs_properties_add_path( + props, + SETTINGS_LAYOUT_PATH, + cstr!("Layout"), + OBS_PATH_FILE, + cstr!("LiveSplit Layouts (*.lsl *.ls1l)"), + ptr::null(), + ); + obs_properties_add_button( + props, + SETTINGS_SAVE_SPLITS, + cstr!("Save Splits"), + Some(save_splits), + ); + props +} + +unsafe extern "C" fn get_defaults(settings: *mut obs_data_t) { + obs_data_set_default_int(settings, SETTINGS_WIDTH, 300); + obs_data_set_default_int(settings, SETTINGS_HEIGHT, 500); +} + +fn default_run() -> Run { + let mut run = Run::new(); + run.push_segment(Segment::new("Time")); + run +} + +unsafe extern "C" fn update(data: *mut c_void, settings: *mut obs_data_t) { + let state: &mut State = &mut *data.cast(); + let settings = parse_settings(settings); + state.timer.set_run(settings.run).unwrap(); + state.layout = settings.layout; + if state.width != settings.width || state.height != settings.height { + state.width = settings.width; + state.height = settings.height; + state + .renderer + .render(&state.state, [state.width, state.height]); + + let mut arr = [state.renderer.image().as_ptr(), ptr::null()]; + obs_enter_graphics(); + let mut texture = gs_texture_create( + state.width, + state.height, + GS_RGBA, + 1, + arr.as_mut_ptr(), + GS_DYNAMIC, + ); + mem::swap(&mut state.texture, &mut texture); + gs_texture_destroy(texture); + obs_leave_graphics(); + } +} + +#[no_mangle] +pub extern "C" fn obs_module_load() -> bool { + static SOURCE_INFO: Lazy> = Lazy::new(|| { + UnsafeMultiThread(unsafe { + obs_source_info { + id: cstr!("livesplit-one"), + type_: OBS_SOURCE_TYPE_INPUT, + output_flags: OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_INTERACTION, + get_name: Some(get_name), + create: Some(create), + destroy: Some(destroy), + get_width: Some(get_width), + get_height: Some(get_height), + video_render: Some(video_render), + mouse_wheel: Some(mouse_wheel), + get_properties: Some(get_properties), + get_defaults: Some(get_defaults), + update: Some(update), + icon_type: OBS_ICON_TYPE_GAME_CAPTURE, + ..mem::zeroed() + } + }) + }); + + let source_info: &obs_source_info = &SOURCE_INFO.0; + + unsafe { + obs_register_source_s(source_info, mem::size_of_val(source_info) as _); + } + true +}