diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c280d14..096a510 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,36 +18,62 @@ jobs: steps: - name: Download apt packages run: | - sudo apt-get update -y && sudo apt-get install -y libsfml-dev libcsfml-dev libasound2-dev libgtk-3-dev libatk1.0-dev libpango1.0-dev libudev-dev + sudo apt-get update -y && sudo apt-get install -y libasound2-dev libudev-dev - - name: Install cargo-tarpaulin - uses: actions-rs/install@v0.1 + - uses: actions-rs/toolchain@v1 with: - crate: cargo-tarpaulin - version: latest - use-tool-cache: true + profile: minimal + toolchain: stable + override: true + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov - - uses: actions/checkout@v2 + - name: Use sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 - - uses: actions-rs/audit-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v4 + + # breaks for now since Cargo.lock is in a new version (4) + # - uses: actions-rs/audit-check@v1 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} - name: Build - run: cargo build --verbose - - - name: Run test cargo-tarpaulin - run: cargo tarpaulin -t 300 --workspace -e nes_ui_native_windows nes_ui_sfml nes_ui_gtk nes_ui_tui -v -o Xml - - - uses: codecov/codecov-action@v1.0.11 - - # For some reason windows crashes when running the emulator tests - #windows: - # runs-on: windows-latest - # - # steps: - # - uses: actions/checkout@v2 - # - name: Build native windows UI - # run: cargo build -p nes_ui_native_windows --verbose - # - name: Run tests - # run: cargo test --workspace --exclude nes_ui_gtk --exclude nes_ui_sfml --lib + run: cargo build --verbose --all --all-targets --profile=ci + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + + # runs cargo with defaults flags, using the default `lcov` output + - name: Test + run: cargo llvm-cov --all-features --workspace --exclude plastic_ui --exclude plastic_ui_tui --lcov --output-path lcov.info + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + + # afterwards, upload the report to codecov + - uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: lcov.info + fail_ci_if_error: true + + windows: + runs-on: windows-latest + + steps: + + - name: Use sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - uses: actions/checkout@v4 + - name: Build + run: cargo build --all --all-targets --profile=ci + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + - name: Test + run: cargo test --workspace --exclude plastic_ui --exclude plastic_ui_tui --lib + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + diff --git a/Cargo.lock b/Cargo.lock index 58a1285..c4b0da6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1669 +1,4914 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "aho-corasick" -version = "0.7.15" +name = "ab_glyph" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "79faae4620f45232f599d9bc7b290f88247a0834162c4495ab2f02d60004adfb" dependencies = [ - "memchr", + "ab_glyph_rasterizer", + "owned_ttf_parser", ] [[package]] -name = "alsa-sys" -version = "0.1.2" +name = "ab_glyph_rasterizer" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58" -dependencies = [ - "libc", - "pkg-config", -] +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] -name = "anyhow" -version = "1.0.37" +name = "accesskit" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" +checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" [[package]] -name = "atk" -version = "0.9.0" +name = "accesskit_consumer" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812b4911e210bd51b24596244523c856ca749e6223c50a7fbbba3f89ee37c426" +checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" dependencies = [ - "atk-sys", - "bitflags", - "glib", - "glib-sys", - "gobject-sys", - "libc", + "accesskit", ] [[package]] -name = "atk-sys" -version = "0.10.0" +name = "accesskit_macos" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f530e4af131d94cc4fa15c5c9d0348f0ef28bac64ba660b6b2a1cf2605dedfce" +checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "accesskit", + "accesskit_consumer", + "objc2 0.3.0-beta.3.patch-leaks.3", + "once_cell", ] [[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - -[[package]] -name = "bincode" -version = "1.3.1" +name = "accesskit_unix" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08" dependencies = [ - "byteorder", + "accesskit", + "accesskit_consumer", + "async-channel", + "async-once-cell", + "atspi", + "futures-lite 1.13.0", + "once_cell", "serde", + "zbus 3.15.2", ] [[package]] -name = "bindgen" -version = "0.56.0" +name = "accesskit_windows" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", + "accesskit", + "accesskit_consumer", + "once_cell", + "paste", + "static_assertions", + "windows 0.48.0", ] [[package]] -name = "bitflags" -version = "1.2.1" +name = "accesskit_winit" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "winit", +] [[package]] -name = "bumpalo" -version = "3.4.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "byteorder" -version = "1.3.4" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] -name = "cairo-rs" -version = "0.9.1" +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c0f2e047e8ca53d0ff249c54ae047931d7a6ebe05d00af73e0ffeb6e34bdb8" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "bitflags", - "cairo-sys-rs", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "thiserror", + "cfg-if 1.0.0", + "getrandom", + "once_cell", + "version_check", + "zerocopy", ] [[package]] -name = "cairo-sys-rs" -version = "0.10.0" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed2639b9ad5f1d6efa76de95558e11339e7318426d84ac4890b86c03e828ca7" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "memchr", ] [[package]] -name = "cassowary" -version = "0.3.0" +name = "allocator-api2" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] -name = "cc" -version = "1.0.66" +name = "alsa" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.6.0", + "cfg-if 1.0.0", + "libc", +] [[package]] -name = "cexpr" -version = "0.4.0" +name = "alsa-sys" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" dependencies = [ - "nom", + "libc", + "pkg-config", ] [[package]] -name = "cfg-if" -version = "0.1.10" +name = "android-activity" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +dependencies = [ + "android-properties", + "bitflags 2.6.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "android-properties" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] -name = "clang-sys" -version = "1.0.3" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "glob", "libc", - "libloading", ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "arboard" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" dependencies = [ - "bitflags", + "clipboard-win", + "log", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "x11rb", ] [[package]] -name = "core-foundation" -version = "0.6.4" +name = "arrayref" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] -name = "core-foundation-sys" -version = "0.6.2" +name = "arrayvec" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "coreaudio-rs" -version = "0.9.1" +name = "as-raw-xcb-connection" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" -dependencies = [ - "bitflags", - "coreaudio-sys", -] +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] -name = "coreaudio-sys" -version = "0.2.8" +name = "ash" +version = "0.37.3+1.3.251" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" dependencies = [ - "bindgen", + "libloading 0.7.4", ] [[package]] -name = "cpal" -version = "0.11.0" +name = "ashpd" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b55d55d69f403f62a95bd3c04b431e0aedf5120c70f15d07a8edd234443dd59" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" dependencies = [ - "alsa-sys", - "core-foundation-sys", - "coreaudio-rs", - "lazy_static", - "libc", - "num-traits", - "stdweb 0.1.3", - "thiserror", - "winapi", + "async-fs 2.1.2", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "url", + "zbus 4.4.0", ] [[package]] -name = "crossterm" -version = "0.17.7" +name = "async-broadcast" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" dependencies = [ - "bitflags", - "crossterm_winapi", - "lazy_static", - "libc", - "mio", - "parking_lot 0.10.2", - "signal-hook", - "winapi", + "event-listener 2.5.3", + "futures-core", ] [[package]] -name = "crossterm" -version = "0.18.2" +name = "async-broadcast" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "bitflags", - "crossterm_winapi", - "lazy_static", - "libc", - "mio", - "parking_lot 0.11.1", - "signal-hook", - "winapi", + "event-listener 5.3.1", + "event-listener-strategy", + "futures-core", + "pin-project-lite", ] [[package]] -name = "crossterm_winapi" -version = "0.6.2" +name = "async-channel" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ - "winapi", + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", ] [[package]] -name = "csfml-audio-sys" -version = "0.5.0" +name = "async-executor" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b928d3b663c6d4d45c330a254b432e3a228cba3851207bda5542fb9b978a8242" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ - "csfml-system-sys", - "sfml-build", + "async-task", + "concurrent-queue", + "fastrand 2.1.1", + "futures-lite 2.3.0", + "slab", ] [[package]] -name = "csfml-graphics-sys" -version = "0.5.0" +name = "async-fs" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4f0c658045598446f1968df8acf047787e70dc44962686f54ec0d9835aed3e" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "csfml-system-sys", - "csfml-window-sys", - "sfml-build", + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", ] [[package]] -name = "csfml-system-sys" -version = "0.5.0" +name = "async-fs" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ebeefc2da7f0568d54eb798706bbb1aa6fd589cda7a84c1188b860773ace35d" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ - "sfml-build", + "async-lock 3.4.0", + "blocking", + "futures-lite 2.3.0", ] [[package]] -name = "csfml-window-sys" -version = "0.5.0" +name = "async-io" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8d000d0a6646167f7ccece9855718145c3e25ab738c9bc8ffeedfa17c41dd3" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "csfml-system-sys", - "sfml-build", + "async-lock 2.8.0", + "autocfg", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.5.2", + "rustix 0.37.13", + "slab", + "socket2", + "waker-fn", ] [[package]] -name = "directories-next" -version = "2.0.0" +name = "async-io" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ + "async-lock 3.4.0", "cfg-if 1.0.0", - "dirs-sys-next", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.3", + "rustix 0.38.36", + "slab", + "tracing", + "windows-sys 0.59.0", ] [[package]] -name = "dirs-sys-next" -version = "0.1.1" +name = "async-lock" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "libc", - "redox_users", - "winapi", + "event-listener 2.5.3", ] [[package]] -name = "discard" -version = "1.0.4" +name = "async-lock" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] [[package]] -name = "either" -version = "1.6.1" +name = "async-net" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io 2.3.4", + "blocking", + "futures-lite 2.3.0", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "async-once-cell" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "9338790e78aa95a416786ec8389546c4b6a1dfc3dc36071ed9518a9413a542eb" [[package]] -name = "futures" -version = "0.3.8" +name = "async-process" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if 1.0.0", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.36", + "windows-sys 0.48.0", ] [[package]] -name = "futures-channel" -version = "0.3.8" +name = "async-process" +version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374" dependencies = [ - "futures-core", - "futures-sink", + "async-channel", + "async-io 2.3.4", + "async-lock 3.4.0", + "async-signal", + "async-task", + "blocking", + "cfg-if 1.0.0", + "event-listener 5.3.1", + "futures-lite 2.3.0", + "rustix 0.38.36", + "tracing", + "windows-sys 0.59.0", ] [[package]] -name = "futures-core" -version = "0.3.8" +name = "async-recursion" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] [[package]] -name = "futures-executor" -version = "0.3.8" +name = "async-signal" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ + "async-io 2.3.4", + "async-lock 3.4.0", + "atomic-waker", + "cfg-if 1.0.0", "futures-core", - "futures-task", - "futures-util", + "futures-io", + "rustix 0.38.36", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", ] [[package]] -name = "futures-io" -version = "0.3.8" +name = "async-task" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] -name = "futures-macro" -version = "0.3.8" +name = "async-trait" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] -name = "futures-sink" -version = "0.3.8" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "futures-task" -version = "0.3.8" +name = "atspi" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" dependencies = [ - "once_cell", + "atspi-common", + "atspi-connection", + "atspi-proxies", ] [[package]] -name = "futures-util" -version = "0.3.8" +name = "atspi-common" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", + "enumflags2", + "serde", + "static_assertions", + "zbus 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", ] [[package]] -name = "gdk" -version = "0.13.2" +name = "atspi-connection" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db00839b2a68a7a10af3fa28dfb3febaba3a20c3a9ac2425a33b7df1f84a6b7d" +checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" dependencies = [ - "bitflags", - "cairo-rs", - "cairo-sys-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "pango", + "atspi-common", + "atspi-proxies", + "futures-lite 1.13.0", + "zbus 3.15.2", ] [[package]] -name = "gdk-pixbuf" -version = "0.9.0" +name = "atspi-proxies" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6dae3cb99dd49b758b88f0132f8d401108e63ae8edd45f432d42cdff99998a" +checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" dependencies = [ - "gdk-pixbuf-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", + "atspi-common", + "serde", + "zbus 3.15.2", ] [[package]] -name = "gdk-pixbuf-sys" -version = "0.10.0" +name = "autocfg" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bfe468a7f43e97b8d193a762b6c5cf67a7d36cacbc0b9291dbcae24bfea1e8f" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] -name = "gdk-sys" -version = "0.10.0" +name = "base-x" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9653cfc500fd268015b1ac055ddbc3df7a5c9ea3f4ccef147b3957bd140d69" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] -name = "getrandom" -version = "0.1.15" +name = "bincode" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" dependencies = [ - "cfg-if 0.1.10", - "libc", - "wasi", + "byteorder", + "serde", ] [[package]] -name = "gilrs" -version = "0.7.4" +name = "bindgen" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122bb249f904e5f4ac73fc514b9b2ce6cce3af511f5df00ffc8000e47de6b290" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "fnv", - "gilrs-core", - "log", - "stdweb 0.4.20", - "uuid", - "vec_map", + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.77", ] [[package]] -name = "gilrs-core" -version = "0.2.6" +name = "bit-set" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c758daf46af26d6872fe55507e3b2339779a160a06ad7a9b2a082f221209cd" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "core-foundation", - "io-kit-sys", - "libc", - "libudev-sys", - "log", - "nix", - "rusty-xinput", - "stdweb 0.4.20", - "uuid", - "vec_map", - "winapi", + "bit-vec", ] [[package]] -name = "gio" -version = "0.9.1" +name = "bit-vec" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb60242bfff700772dae5d9e3a1f7aa2e4ebccf18b89662a16acb2822568561" -dependencies = [ - "bitflags", - "futures", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "thiserror", -] +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] -name = "gio-sys" -version = "0.10.1" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e24fb752f8f5d2cf6bbc2c606fd2bc989c81c5e2fe321ab974d54f8b6344eac" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "glib" -version = "0.10.3" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" -dependencies = [ - "bitflags", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", -] +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] -name = "glib-macros" -version = "0.10.1" +name = "block" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" -dependencies = [ - "anyhow", - "heck", - "itertools", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "glib-sys" -version = "0.10.1" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "libc", - "system-deps", + "generic-array", ] [[package]] -name = "glob" -version = "0.3.0" +name = "block-sys" +version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys 0.2.0-beta.2", +] [[package]] -name = "gobject-sys" -version = "0.10.0" +name = "block-sys" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "objc-sys 0.3.5", ] [[package]] -name = "gtk" -version = "0.9.2" +name = "block2" +version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f022f2054072b3af07666341984562c8e626a79daa8be27b955d12d06a5ad6a" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" dependencies = [ - "atk", - "bitflags", - "cairo-rs", - "cairo-sys-rs", - "cc", - "gdk", - "gdk-pixbuf", - "gdk-pixbuf-sys", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk-sys", - "libc", - "once_cell", - "pango", - "pango-sys", - "pkg-config", + "block-sys 0.1.0-beta.1", + "objc2-encode 2.0.0-pre.2", ] [[package]] -name = "gtk-sys" -version = "0.10.0" +name = "block2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89acda6f084863307d948ba64a4b1ef674e8527dddab147ee4cdcc194c880457" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", + "block-sys 0.2.1", + "objc2 0.4.1", ] [[package]] -name = "heck" -version = "0.3.2" +name = "block2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "unicode-segmentation", + "objc2 0.5.2", ] [[package]] -name = "instant" -version = "0.1.9" +name = "blocking" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "cfg-if 1.0.0", + "async-channel", + "async-task", + "futures-io", + "futures-lite 2.3.0", + "piper", ] [[package]] -name = "io-kit-sys" -version = "0.1.0" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0" -dependencies = [ - "core-foundation-sys", - "mach", -] +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "itertools" -version = "0.9.0" +name = "bytemuck" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" dependencies = [ - "either", + "bytemuck_derive", ] [[package]] -name = "itoa" -version = "0.4.7" +name = "bytemuck_derive" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "lazycell" -version = "1.3.0" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] -name = "libc" -version = "0.2.81" +name = "bytes" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] -name = "libloading" -version = "0.6.6" +name = "calloop" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "cfg-if 1.0.0", - "winapi", + "bitflags 2.6.0", + "log", + "polling 3.7.3", + "rustix 0.38.36", + "slab", + "thiserror", ] [[package]] -name = "libm" -version = "0.1.4" +name = "calloop" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.6.0", + "log", + "polling 3.7.3", + "rustix 0.38.36", + "slab", + "thiserror", +] [[package]] -name = "libudev-sys" -version = "0.1.4" +name = "calloop-wayland-source" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" dependencies = [ - "libc", - "pkg-config", + "calloop 0.12.4", + "rustix 0.38.36", + "wayland-backend", + "wayland-client", ] [[package]] -name = "lock_api" -version = "0.3.4" +name = "calloop-wayland-source" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "scopeguard", + "calloop 0.13.0", + "rustix 0.38.36", + "wayland-backend", + "wayland-client", ] [[package]] -name = "lock_api" -version = "0.4.2" +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ - "scopeguard", + "rustversion", ] [[package]] -name = "log" -version = "0.4.11" +name = "cc" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" dependencies = [ - "cfg-if 0.1.10", + "jobserver", + "libc", + "shlex", ] [[package]] -name = "mach" -version = "0.2.3" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "libc", + "nom", ] [[package]] -name = "memchr" -version = "2.3.4" +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "mio" -version = "0.7.7" +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cgl" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" dependencies = [ "libc", - "log", - "miow", - "ntapi", - "winapi", ] [[package]] -name = "miow" -version = "0.3.6" +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ - "socket2", - "winapi", + "glob", + "libc", + "libloading 0.8.5", ] [[package]] -name = "native-windows-derive" -version = "1.0.2" +name = "clipboard-win" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e12bdd46113e604a98d04f19f79249e1679be21a65eaa1dbadec16ba00c94f7" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ - "proc-macro2", - "quote", - "syn", + "error-code", ] [[package]] -name = "native-windows-gui" -version = "1.0.7" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe8d44e6cea6bba40a302d1ab3ee50c6d9f9714ab94a776b0db0a5521c49c9ce" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "bitflags", - "lazy_static", - "stretch", - "winapi", - "winapi-build", + "termcolor", + "unicode-width", ] [[package]] -name = "nes_ui_gtk" -version = "0.1.0" +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" dependencies = [ - "cairo-rs", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk", - "plastic_core", + "com_macros", ] [[package]] -name = "nes_ui_native_windows" -version = "0.1.0" +name = "com_macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" dependencies = [ - "native-windows-derive", - "native-windows-gui", - "plastic_core", - "winapi", + "com_macros_support", + "proc-macro2", + "syn 1.0.109", ] [[package]] -name = "nes_ui_sfml" -version = "0.1.0" +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" dependencies = [ - "plastic_core", - "sfml", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "nes_ui_tui" -version = "0.1.0" +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "crossterm 0.17.7", - "gilrs", - "plastic_core", - "tui", + "bytes", + "memchr", ] [[package]] -name = "nix" -version = "0.15.0" +name = "compact_str" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", - "void", + "castaway", + "cfg-if 1.0.0", + "itoa", + "rustversion", + "ryu", + "static_assertions", ] [[package]] -name = "nom" -version = "5.1.2" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ - "memchr", - "version_check", + "crossbeam-utils", ] [[package]] -name = "ntapi" -version = "0.3.6" +name = "core-foundation" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" dependencies = [ - "winapi", + "core-foundation-sys 0.6.2", + "libc", ] [[package]] -name = "num-traits" -version = "0.2.14" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "autocfg", + "core-foundation-sys 0.8.7", + "libc", ] [[package]] -name = "once_cell" -version = "1.5.2" +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "pango" -version = "0.9.1" +name = "core-graphics" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9937068580bebd8ced19975938573803273ccbcbd598c58d4906efd4ac87c438" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ - "bitflags", - "glib", - "glib-sys", - "gobject-sys", + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types", + "foreign-types", "libc", - "once_cell", - "pango-sys", ] [[package]] -name = "pango-sys" -version = "0.10.0" +name = "core-graphics-types" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d2650c8b62d116c020abd0cea26a4ed96526afda89b1c4ea567131fdefc890" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ - "glib-sys", - "gobject-sys", + "bitflags 1.3.2", + "core-foundation 0.9.4", "libc", - "system-deps", ] [[package]] -name = "parking_lot" -version = "0.10.2" +name = "coreaudio-rs" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.7.2", + "bitflags 1.3.2", + "core-foundation-sys 0.8.7", + "coreaudio-sys", ] [[package]] -name = "parking_lot" -version = "0.11.1" +name = "coreaudio-sys" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" dependencies = [ - "instant", - "lock_api 0.4.2", - "parking_lot_core 0.8.2", + "bindgen", ] [[package]] -name = "parking_lot_core" -version = "0.7.2" +name = "cpal" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "alsa", + "core-foundation-sys 0.8.7", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", "libc", - "redox_syscall", - "smallvec", - "winapi", + "mach2", + "ndk", + "ndk-context", + "oboe", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.54.0", ] [[package]] -name = "parking_lot_core" -version = "0.8.2" +name = "cpufeatures" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ - "cfg-if 1.0.0", - "instant", "libc", - "redox_syscall", - "smallvec", - "winapi", ] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "crc32fast" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if 1.0.0", +] [[package]] -name = "pin-project" -version = "1.0.2" +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crossterm" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "pin-project-internal", + "bitflags 2.6.0", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix 0.38.36", + "signal-hook", + "signal-hook-mio", + "winapi", ] [[package]] -name = "pin-project-internal" -version = "1.0.2" +name = "crossterm_winapi" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "winapi", ] [[package]] -name = "pin-utils" -version = "0.1.0" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] [[package]] -name = "pkg-config" -version = "0.3.19" +name = "cursor-icon" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] -name = "plastic_core" -version = "0.1.0" +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "bincode", - "bitflags", - "directories-next", - "regex", - "rodio", - "serde", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "proc-macro-crate" -version = "0.1.5" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "toml", + "block-buffer", + "crypto-common", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "directories" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", + "dirs-sys", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "dirs-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", ] [[package]] -name = "proc-macro-hack" -version = "0.5.19" +name = "discard" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] -name = "proc-macro-nested" -version = "0.1.6" +name = "dispatch" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] -name = "proc-macro2" -version = "1.0.24" +name = "dlib" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "unicode-xid", + "libloading 0.8.5", ] [[package]] -name = "quote" -version = "1.0.7" +name = "document-features" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" dependencies = [ - "proc-macro2", + "litrs", ] [[package]] -name = "redox_syscall" -version = "0.1.57" +name = "downcast-rs" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] -name = "redox_users" -version = "0.3.5" +name = "dynwave" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +checksum = "daabe4015db7e0b48e5140e45b43414027a498b99206226feda5e61405782ef5" dependencies = [ - "getrandom", - "redox_syscall", + "cpal", + "ringbuf", + "rubato", ] [[package]] -name = "regex" -version = "1.4.2" +name = "ecolor" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "2e6b451ff1143f6de0f33fc7f1b68fecfd2c7de06e104de96c4514de3f5396f8" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", + "bytemuck", + "emath", ] [[package]] -name = "regex-syntax" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" - -[[package]] -name = "rodio" -version = "0.11.0" +name = "eframe" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73bbf260262fd5501b7a17d6827e0d25c1127e921eb177150a060faf6e217a70" +checksum = "6490ef800b2e41ee129b1f32f9ac15f713233fe3bc18e241a1afe1e4fb6811e0" dependencies = [ - "cpal", - "lazy_static", + "ahash", + "bytemuck", + "document-features", + "egui", + "egui-wgpu", + "egui-winit", + "egui_glow", + "glow", + "glutin", + "glutin-winit", + "image", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winapi", + "winit", ] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "egui" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "20c97e70a2768de630f161bb5392cbd3874fcf72868f14df0e002e82e06cb798" +dependencies = [ + "accesskit", + "ahash", + "emath", + "epaint", + "log", + "nohash-hasher", +] [[package]] -name = "rustc_version" -version = "0.2.3" +name = "egui-wgpu" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "47c7a7c707877c3362a321ebb4f32be811c0b91f7aebf345fb162405c0218b4c" dependencies = [ - "semver", + "ahash", + "bytemuck", + "document-features", + "egui", + "epaint", + "log", + "thiserror", + "type-map", + "web-time", + "wgpu", + "winit", ] [[package]] -name = "rusty-xinput" -version = "1.2.0" +name = "egui-winit" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2aa654bc32eb9ca14cce1a084abc9dfe43949a4547c35269a094c39272db3bb" +checksum = "fac4e066af341bf92559f60dbdf2020b2a03c963415349af5f3f8d79ff7a4926" dependencies = [ - "lazy_static", + "accesskit_winit", + "ahash", + "arboard", + "egui", "log", - "winapi", + "raw-window-handle 0.6.2", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", ] [[package]] -name = "ryu" -version = "1.0.5" +name = "egui_glow" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "4e2bdc8b38cfa17cc712c4ae079e30c71c00cd4c2763c9e16dc7860a02769103" +dependencies = [ + "ahash", + "bytemuck", + "egui", + "glow", + "log", + "memoffset 0.9.1", + "wasm-bindgen", + "web-sys", + "winit", +] [[package]] -name = "scopeguard" -version = "1.1.0" +name = "either" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "semver" -version = "0.9.0" +name = "emath" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "0a6a21708405ea88f63d8309650b4d77431f4bc28fb9d8e6f77d3963b51249e6" dependencies = [ - "semver-parser", + "bytemuck", ] [[package]] -name = "semver-parser" -version = "0.7.0" +name = "endi" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] -name = "serde" -version = "1.0.118" +name = "enumflags2" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ - "serde_derive", + "enumflags2_derive", + "serde", ] [[package]] -name = "serde_derive" -version = "1.0.118" +name = "enumflags2_derive" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] -name = "serde_json" -version = "1.0.61" +name = "epaint" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "3f0dcc0a0771e7500e94cd1cb797bd13c9f23b9409bdc3c824e2cbc562b7fa01" dependencies = [ - "itoa", - "ryu", - "serde", + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "log", + "nohash-hasher", + "parking_lot", ] [[package]] -name = "sfml" -version = "0.15.1" +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513d5e9ff2e56ae75c7ab943fbd15880fd685230d9ddc0abb853c1374ee66129" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "bitflags", - "csfml-audio-sys", - "csfml-graphics-sys", - "csfml-system-sys", - "csfml-window-sys", - "widestring", + "libc", + "windows-sys 0.52.0", ] [[package]] -name = "sfml-build" -version = "0.3.0" +name = "error-code" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f83a1ad7f7c79c9273019b2abfacbd4fa7150d23caca8cae586f0f1ea43b151" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] -name = "sha1" -version = "0.6.0" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "shlex" -version = "0.1.1" +name = "event-listener" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] [[package]] -name = "signal-hook" -version = "0.1.17" +name = "event-listener" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ - "libc", - "mio", - "signal-hook-registry", + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] -name = "signal-hook-registry" -version = "1.3.0" +name = "event-listener-strategy" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "libc", + "event-listener 5.3.1", + "pin-project-lite", ] [[package]] -name = "slab" -version = "0.4.2" +name = "fastrand" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] [[package]] -name = "smallvec" -version = "1.5.1" +name = "fastrand" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] -name = "socket2" -version = "0.3.19" +name = "fdeflate" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi", + "simd-adler32", ] [[package]] -name = "stdweb" -version = "0.1.3" +name = "flate2" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] [[package]] -name = "stdweb" -version = "0.4.20" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "serde", - "serde_json", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "stdweb-derive" -version = "0.5.3" +name = "foldhash" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", + "foreign-types-macros", + "foreign-types-shared", ] [[package]] -name = "stdweb-internal-macros" -version = "0.2.9" +name = "foreign-types-macros" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "base-x", "proc-macro2", "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", + "syn 2.0.77", ] [[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" +name = "foreign-types-shared" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] -name = "stretch" -version = "0.3.2" +name = "form_urlencoded" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "lazy_static", - "libm", + "percent-encoding", ] [[package]] -name = "strum" -version = "0.18.0" +name = "futures-channel" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] [[package]] -name = "strum_macros" -version = "0.18.0" +name = "futures-core" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] -name = "syn" -version = "1.0.54" +name = "futures-io" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] -name = "system-deps" -version = "1.3.2" +name = "futures-lite" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "heck", - "pkg-config", - "strum", - "strum_macros", - "thiserror", - "toml", - "version-compare", + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", ] [[package]] -name = "thiserror" -version = "1.0.22" +name = "futures-lite" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "thiserror-impl", + "fastrand 2.1.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", ] [[package]] -name = "thiserror-impl" -version = "1.0.22" +name = "futures-macro" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] -name = "thread_local" -version = "1.1.4" +name = "futures-sink" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] -name = "toml" -version = "0.5.8" +name = "futures-task" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gilrs" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122bb249f904e5f4ac73fc514b9b2ce6cce3af511f5df00ffc8000e47de6b290" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "stdweb", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c758daf46af26d6872fe55507e3b2339779a160a06ad7a9b2a082f221209cd" +dependencies = [ + "core-foundation 0.6.4", + "io-kit-sys", + "libc", + "libudev-sys", + "log", + "nix 0.15.0", + "rusty-xinput", + "stdweb", + "uuid", + "vec_map", + "winapi", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" +dependencies = [ + "bitflags 2.6.0", + "cfg_aliases 0.1.1", + "cgl", + "core-foundation 0.9.4", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "icrate", + "libloading 0.8.5", + "objc2 0.4.1", + "once_cell", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +dependencies = [ + "cfg_aliases 0.1.1", + "glutin", + "raw-window-handle 0.5.2", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +dependencies = [ + "gl_generator", + "windows-sys 0.48.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.6.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +dependencies = [ + "log", + "presser", + "thiserror", + "winapi", + "windows 0.52.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" +dependencies = [ + "bitflags 2.6.0", + "gpu-descriptor-types", + "hashbrown 0.14.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hassle-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.6.0", + "com", + "libc", + "libloading 0.8.5", + "thiserror", + "widestring", + "winapi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2 0.3.0", + "dispatch", + "objc2 0.4.1", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "instability" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "io-kit-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0" +dependencies = [ + "core-foundation-sys 0.6.2", + "mach", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if 1.0.0", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.5", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if 1.0.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.0", +] + +[[package]] +name = "mach" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +dependencies = [ + "libc", +] + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "naga" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.6.0", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "cfg_aliases 0.2.1", + "libc", + "memoffset 0.9.1", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys 0.3.5", + "objc2-encode 3.0.0", +] + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys 0.3.5", + "objc2-encode 4.0.3", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "oboe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" +dependencies = [ + "jni", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" +dependencies = [ + "cc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox 0.0.2", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490d3a563d3122bf7c911a59b0add9389e5ec0f5f0c3ac6b91ff235a0e6a7f90" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.5.3", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand 2.1.1", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plastic_core" +version = "0.1.0" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "serde", +] + +[[package]] +name = "plastic_ui" +version = "0.1.0" +dependencies = [ + "directories", + "dynwave", + "eframe", + "egui", + "egui-winit", + "plastic_core", + "rfd", +] + +[[package]] +name = "plastic_ui_tui" +version = "0.1.0" +dependencies = [ + "crossterm", + "dynwave", + "gilrs", + "plastic_core", + "ratatui", +] + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.4", +] + +[[package]] +name = "polling" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "windows-sys 0.42.0", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if 1.0.0", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.36", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.20", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" + +[[package]] +name = "quick-xml" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ratatui" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" +dependencies = [ + "bitflags 2.6.0", + "cassowary", + "compact_str", + "crossterm", + "instability", + "itertools 0.13.0", + "lru", + "paste", + "strum", + "strum_macros", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "realfft" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953d9f7e5cdd80963547b456251296efc2626ed4e3cbf36c869d9564e0220571" +dependencies = [ + "rustfft", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox 0.1.3", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.6.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ringbuf" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79abed428d1fd2a128201cec72c5f6938e2da607c6f3745f769fabea399d950a" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "rubato" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6dd52e80cfc21894deadf554a5673002938ae4625f7a283e536f9cf7c17b0d5" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "realfft", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustfft" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43806561bc506d0c5d160643ad742e3161049ac01027b5e6d7524091fd401d86" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", + "version_check", +] + +[[package]] +name = "rustix" +version = "0.37.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "rusty-xinput" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3335c2b62e1e48dd927f6c8941705386e3697fa944aabcb10431bea7ee47ef3" +dependencies = [ + "lazy_static", + "log", + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[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-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit 0.18.1", + "tiny-skia", +] + +[[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.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbcf9b78a125ee667ae19388837dd12294b858d101fdd393cb9d5501ef09eb2" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.6.0", + "calloop 0.12.4", + "calloop-wayland-source 0.2.0", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.36", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols 0.31.2", + "wayland-protocols-wlr 0.2.0", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.6.0", + "calloop 0.13.0", + "calloop-wayland-source 0.3.0", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.36", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols 0.32.4", + "wayland-protocols-wlr 0.3.4", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +dependencies = [ + "libc", + "smithay-client-toolkit 0.19.2", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "serde", + "serde_json", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn 1.0.109", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1 0.6.1", + "syn 1.0.109", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if 1.0.0", + "fastrand 2.1.1", + "once_cell", + "rustix 0.38.36", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.6.18", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "ttf-parser" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" + +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.36", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +dependencies = [ + "bitflags 2.6.0", + "rustix 0.38.36", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.6.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" +dependencies = [ + "rustix 0.38.36", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.4", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "425ba64c1e13b1c6e8c5d2541c8fac10022ca584f33da781db01b5756aef1f4e" +dependencies = [ + "block2 0.5.1", + "core-foundation 0.9.4", + "home", + "jni", + "log", + "ndk-context", + "objc2 0.5.2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "wgpu" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e37c7b9921b75dfd26dd973fdcbce36f13dfa6e2dc82aece584e0ed48c355c" +dependencies = [ + "arrayvec", + "cfg-if 1.0.0", + "cfg_aliases 0.1.1", + "document-features", + "js-sys", + "log", + "parking_lot", + "profiling", + "raw-window-handle 0.6.2", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.6.0", + "cfg_aliases 0.1.1", + "codespan-reporting", + "document-features", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.2", + "rustc-hash", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bitflags 2.6.0", + "cfg_aliases 0.1.1", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.5", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.2", + "renderdoc-sys", + "rustc-hash", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +dependencies = [ + "bitflags 2.6.0", + "js-sys", + "web-sys", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[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-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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[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 = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "windows-interface" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" dependencies = [ - "serde", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "tui" -version = "0.13.0" -source = "git+https://github.com/fdehau/tui-rs#eb1e3be7228509e42cbcbaef610e6bd5c5f64ba6" +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "bitflags", - "cassowary", - "crossterm 0.18.2", - "unicode-segmentation", - "unicode-width", + "windows-targets 0.52.6", ] [[package]] -name = "unicode-segmentation" -version = "1.7.1" +name = "windows-sys" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] [[package]] -name = "unicode-width" -version = "0.1.8" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] [[package]] -name = "unicode-xid" -version = "0.2.1" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] [[package]] -name = "uuid" -version = "0.8.1" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] -name = "vec_map" -version = "0.8.2" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] [[package]] -name = "version-compare" -version = "0.0.10" +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] [[package]] -name = "version_check" -version = "0.9.2" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] [[package]] -name = "void" -version = "1.0.2" +name = "windows_aarch64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +name = "windows_aarch64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "wasm-bindgen" -version = "0.2.69" +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winit" +version = "0.29.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.6.0", + "bytemuck", + "calloop 0.12.4", + "cfg_aliases 0.1.1", + "core-foundation 0.9.4", + "core-graphics", + "cursor-icon", + "icrate", + "js-sys", + "libc", + "log", + "memmap2", + "ndk", + "ndk-sys", + "objc2 0.4.1", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "redox_syscall 0.3.5", + "rustix 0.38.36", + "sctk-adwaita", + "smithay-client-toolkit 0.18.1", + "smol_str", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.48.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.69" +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ - "bumpalo", - "lazy_static", + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.5", + "once_cell", + "rustix 0.38.36", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.6.0", + "dlib", "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" + +[[package]] +name = "zbus" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +dependencies = [ + "async-broadcast 0.5.1", + "async-executor", + "async-fs 1.6.0", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process 1.8.1", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1 0.10.6", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast 0.7.1", + "async-executor", + "async-fs 2.1.2", + "async-io 2.3.4", + "async-lock 3.4.0", + "async-process 2.2.4", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.3.1", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1 0.10.6", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_macros" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +dependencies = [ + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn", - "wasm-bindgen-shared", + "regex", + "syn 1.0.109", + "zvariant_utils 1.0.1", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.69" +name = "zbus_macros" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", "quote", - "wasm-bindgen-macro-support", + "syn 2.0.77", + "zvariant_utils 2.1.0", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.69" +name = "zbus_names" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +dependencies = [ + "serde", + "static_assertions", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant 4.2.0", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", + "syn 2.0.77", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.69" +name = "zvariant" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive 3.15.2", +] [[package]] -name = "widestring" -version = "0.4.3" +name = "zvariant" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive 4.2.0", +] [[package]] -name = "winapi" -version = "0.3.9" +name = "zvariant_derive" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils 1.0.1", ] [[package]] -name = "winapi-build" -version = "0.1.1" +name = "zvariant_derive" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.77", + "zvariant_utils 2.1.0", +] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "zvariant_utils" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "zvariant_utils" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/Cargo.toml b/Cargo.toml index e58b243..84e1597 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,13 +2,9 @@ [workspace] resolver = "2" members = [ - "nes_ui_sfml", - "nes_ui_gtk", - "nes_ui_tui", - "nes_ui_native_windows", - "plastic_core", + "plastic_core", "plastic_ui", "plastic_ui_tui", ] -default-members = ["nes_ui_sfml"] +default-members = ["plastic_ui"] [profile.dev] opt-level = 2 @@ -16,3 +12,11 @@ opt-level = 2 [profile.release] opt-level = 3 +[profile.ci] +inherits = "dev" +incremental = false +opt-level = 1 +codegen-units = 16 +debug = false +overflow-checks = false +lto = false diff --git a/README.md b/README.md index a6ad7e9..bbb81f3 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,7 @@ This is a personal project for fun and to experience emulating hardware and conn - [Building](#building) - [Components](#components) - [Interfaces](#interfaces) - - [SFML](#sfml) - - [GTK](#gtk) - - [Native Windows GUI](#native-windows-gui) + - [EGui UI](#ui) - [TUI](#tui) - [Controls](#controls) - [Keyboard](#keyboard) @@ -32,26 +30,16 @@ This is a personal project for fun and to experience emulating hardware and conn #### Pre-built There are pre-built binaries in the [releases](https://github.com/Amjad50/plastic/releases/latest) tab, -Plastic comes in different *UI*s, some work in Windows and Linux and some are for one of them, +Plastic comes in different *UI*s, one which is the most used normal UI built with [egui] and another one which is a terminal UI (`TUI`) built with [ratatui]. check [Interfaces](#interfaces) for more details. #### Building If you want to experience the latest development version, you can build `Plastic` yourself. -You would need [Rust][Rust] itself and any UI dependency for the UI you want to use if needed. For example: - -Recommended UI for linux is [GTK](#gtk): -``` -cargo run --release -p nes_ui_gtk -``` - -Recommended UI for windows is [Native_Windows](#native-windows-gui): ``` -cargo run --release -p nes_ui_native_windows +cargo run --release ``` -> Note: `--release` should be used as running the emulator in `debug` mode would slow it down very much that it would make games unplayable. - ### Components - [x] 6502 CPU, all official and unofficial instructions with accurate timing (without BCD mode). - [x] Picture Processing Unit, almost accurate with some small timing issues that would not effect most games. @@ -80,72 +68,19 @@ cargo run --release -p nes_ui_native_windows controllable using the keyboard and controller (tested with PS4 controller) ### Interfaces -One advantage of this emulator is the great abstraction, for example, adding UI -handlers for the emulator is very easy and straight forward. All you have to do -is to write a [`UiProvider`](./plastic_core/src/lib.rs), and simply -use it with [`NES`](./plastic_core/src/nes.rs) like so: -```rust -use plastic_core::nes::NES; -// Provider implement trait `UiProvider` -let mut nes = NES::new("path/to/rom.nes", Provider {})?; +The main emulator is at [`plastic_core`](./plastic_core/) +And its a struct `NES`, where the UI would clock it, and then +take the resulting audio and pixel buffers to handle them. -nes.run(); -``` +We have 2 UIs, one main and the other just for fun. -For now all UI providers can reset the game through `` - -Using this design, I have implemented some providers which have -different purposes: - -#### SFML -[SFML][SFML] is a game development UI library, it has a good and -easy to use API and is the most performing UI in the list. +#### EGui UI +Simple ui built with [egui] ##### Advantages -1. Designed for Canvas and raw drawing, which makes it faster. -2. Has support for gamepad (only tested with PS4). - -#### GTK -[GTK][GTK] is a casual GUI library that is used to make all kinds -of applications and not designed for games. - -The reason I chose to go with it is for future plans to add a -debugger, which would be a lot easier to add in this UI compared -to [SFML](#sfml). - -From the screenshot, it looks exactly like SFML, and that's because -I haven't added anything specific to GTK yet. But it has almost the -same performance as SFML, so hopefully there will not be much -different in performance even after adding the debugger. - - -##### Advantages -1. Ability to add buttons and menus which enable easier integration - for debuggers and anything similar. -2. Ability to open a NES file through the menu or by dragging the .NES - file into the app. -3. Can run without specifying a ROM from the command line. -4. Ability to pause and resume emulation through `` key and from the menues. - -##### Disadvantages -1. Does not offer gamepad support, but it can be added through - other external libraries. - - -#### Native Windows GUI -Has the same UI and usages as the [GTK](#gtk) version, but is targetted for windows -as GTK require additional libraries to work for windows. - - -##### Advantages (in addition to GTK) -1. Run on the native windows APIs, which is fast and does not need any additional - libraries. - -##### Disadvantages -1. Does not offer gamepad support, but it can be added through - other external libraries. +1. Very simple and easy to use immediate mode UI. #### TUI [![TUI demo](https://img.youtube.com/vi/cMO89-Xljr8/0.jpg)](https://www.youtube.com/watch?v=cMO89-Xljr8) @@ -153,6 +88,10 @@ as GTK require additional libraries to work for windows. This is just for fun, but it is actually working way better than I expected. Check the [demo](https://www.youtube.com/watch?v=cMO89-Xljr8). +If you have one of these terminals mentioned [in this docs](https://docs.rs/crossterm/0.28.1/crossterm/event/struct.PushKeyboardEnhancementFlags.html) +Then you will have a much better experience, since these terminals support detecting button `Release`, normally other terminals don't have this feature, so +the input for this UI can be a bit wonky. + I used [gilrs][gilrs] for gamepad support and its working very nicely, keyboard on the other hand is not very responsive, so it is advised to use gamepad. Also since this uses one character for @@ -205,6 +144,6 @@ For the CPU(6502), [this](https://www.masswerk.at/6502/6502_instruction_set.html [NES-wiki]: https://en.wikipedia.org/wiki/Nintendo_Entertainment_System [Rust]: https://www.rust-lang.org/ -[SFML]: https://www.sfml-dev.org/ -[GTK]: https://www.gtk.org/ [gilrs]: https://gitlab.com/gilrs-project/gilrs +[egui]: https://github.com/emilk/egui +[ratatui]: https://github.com/ratatui/ratatui diff --git a/nes_ui_gtk/Cargo.toml b/nes_ui_gtk/Cargo.toml deleted file mode 100644 index 690ff39..0000000 --- a/nes_ui_gtk/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "nes_ui_gtk" -version = "0.1.0" -authors = ["Amjad Alsharafi "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -plastic_core = { path = "../plastic_core" } - -gtk = "0.9.2" -gio = "0.9.1" -glib = "0.10.2" -gdk = "0.13.2" -gdk-pixbuf = "0.9.0" - -[dependencies.cairo-rs] -version = "0.9.0" -features = ["v1_16", "png"] diff --git a/nes_ui_gtk/src/main.rs b/nes_ui_gtk/src/main.rs deleted file mode 100644 index dd9e22b..0000000 --- a/nes_ui_gtk/src/main.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod ui; -use plastic_core::nes::NES; -use std::env::args; -use ui::GtkProvider; - -fn main() { - let args = args().collect::>(); - - let nes = if args.len() >= 2 { - NES::new(&args[1], GtkProvider::new()) - } else { - Ok(NES::new_without_file(GtkProvider::new())) - }; - - match nes { - Ok(mut nes) => { - nes.run(); - } - Err(err) => { - eprintln!("[ERROR] {}", err); - } - } -} diff --git a/nes_ui_gtk/src/ui.rs b/nes_ui_gtk/src/ui.rs deleted file mode 100644 index 4f7666a..0000000 --- a/nes_ui_gtk/src/ui.rs +++ /dev/null @@ -1,352 +0,0 @@ -use plastic_core::{ - nes_controller::{StandardNESControllerState, StandardNESKey}, - nes_display::{Color as NESColor, TV_HEIGHT, TV_WIDTH}, - BackendEvent, UiEvent, UiProvider, -}; -use std::sync::{ - atomic::AtomicBool, - atomic::Ordering, - mpsc::{Receiver, Sender}, - Arc, Mutex, -}; - -use gdk::{keys::constants as key_vals, keyval_to_upper, DragAction, ModifierType}; -use gdk_pixbuf::prelude::*; -use gdk_pixbuf::PixbufLoader; -use gio::prelude::*; -use glib::source::timeout_add_local; -use gtk::prelude::*; -use gtk::{ - Application, Builder, DestDefaults, DrawingArea, FileChooserAction, FileChooserDialog, - FileFilter, Inhibit, Menu, MenuItem, ResponseType, TargetEntry, TargetFlags, Window, -}; - -const NUMBER_OF_STATES: u8 = 10; - -pub struct GtkProvider { - paused: Arc, -} - -impl GtkProvider { - pub fn new() -> Self { - Self { - paused: Arc::new(AtomicBool::new(false)), - } - } -} - -impl UiProvider for GtkProvider { - fn get_tv_color_converter() -> fn(&NESColor) -> [u8; 4] { - |color| [color.b, color.g, color.r, 0xFF] - } - - fn run_ui_loop( - &mut self, - ui_to_nes_sender: Sender, - nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - ) { - gtk::init().unwrap(); - - let app = Application::new( - Some("amjad50.plastic.nes_gtk"), - gio::ApplicationFlags::NON_UNIQUE, - ) - .expect("Application could not be initialized"); - - let ui_glade_string = include_str!("../ui.glade"); - let builder = Builder::from_string(ui_glade_string); - - let window = builder.get_object::("top_level_window").unwrap(); - let drawing_area = builder.get_object::("canvas").unwrap(); - let menu_action_open = builder.get_object::("menu_action_open").unwrap(); - let save_state_menu_list = builder.get_object::("save_state_menu").unwrap(); - let load_state_menu_list = builder.get_object::("load_state_menu").unwrap(); - let menu_action_quit = builder.get_object::("menu_action_quit").unwrap(); - let menu_action_reset = builder.get_object::("menu_action_reset").unwrap(); - let menu_action_pause = builder.get_object::("menu_action_pause").unwrap(); - let menu_action_resume = builder - .get_object::("menu_action_resume") - .unwrap(); - - // setup window icon - let icon_png_raw_data = include_bytes!("../../images/icon.png"); - let icon_loader = PixbufLoader::with_type("png").unwrap(); - icon_loader.write(icon_png_raw_data).unwrap(); - icon_loader.close().unwrap(); - - if let Some(icon) = icon_loader.get_pixbuf() { - window.set_icon(Some(&icon)); - } - - for i in 1..=NUMBER_OF_STATES { - let save_action = MenuItem::with_mnemonic(&format!("_{} ", i)); - let load_action = MenuItem::with_mnemonic(&format!("_{} ", i)); - - // setup handlers - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - save_action.connect_activate(move |_| { - ui_to_nes_sender.send(UiEvent::SaveState(i)).unwrap(); - }); - } - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - load_action.connect_activate(move |_| { - ui_to_nes_sender.send(UiEvent::LoadState(i)).unwrap(); - }); - } - - // add the actions to the menus - save_state_menu_list.append(&save_action); - load_state_menu_list.append(&load_action); - } - - window.show_all(); - - drawing_area.connect_draw(move |area, cr| { - let data = image.lock().unwrap().to_vec(); - let src = cairo::ImageSurface::create_for_data( - data, - cairo::Format::Rgb24, - TV_WIDTH as i32, - TV_HEIGHT as i32, - cairo::Format::Rgb24 - .stride_for_width(TV_WIDTH as u32) - .unwrap(), - ) - .unwrap(); - let pattern = cairo::SurfacePattern::create(&src); - pattern.set_filter(cairo::Filter::Nearest); - - let area_width = area.get_allocated_width() as f64; - let area_height = area.get_allocated_height() as f64; - - let scale_width = area_width / TV_WIDTH as f64; - let scale_height = area_height / TV_HEIGHT as f64; - - let scale_smallest; - let mut top = 0.; - let mut left = 0.; - - if scale_width > scale_height { - scale_smallest = scale_height; - left = (area_width - (TV_WIDTH as f64 * scale_smallest)) / 2.; - } else { - scale_smallest = scale_width; - top = (area_height - (TV_HEIGHT as f64 * scale_smallest)) / 2.; - }; - - cr.translate(left, top); - - cr.scale(scale_smallest, scale_smallest); - - cr.set_source(&pattern); - cr.paint(); - - Inhibit(false) - }); - - window.set_size_request((TV_WIDTH * 3) as i32, (TV_HEIGHT * 3) as i32); - - { - let ctrl_state = ctrl_state.clone(); - let ui_to_nes_sender = ui_to_nes_sender.clone(); - let paused = self.paused.clone(); - window.connect_key_press_event(move |_, event| { - let mut ctrl = ctrl_state.lock().unwrap(); - - // should be fixed with https://github.com/gtk-rs/gdk/pull/358 - let mut val = event.get_keyval(); - *val = keyval_to_upper(*val); - match val { - key_vals::J => ctrl.press(StandardNESKey::B), - key_vals::K => ctrl.press(StandardNESKey::A), - key_vals::U => ctrl.press(StandardNESKey::Select), - key_vals::I => ctrl.press(StandardNESKey::Start), - key_vals::W => ctrl.press(StandardNESKey::Up), - key_vals::S => ctrl.press(StandardNESKey::Down), - key_vals::A => ctrl.press(StandardNESKey::Left), - key_vals::D => ctrl.press(StandardNESKey::Right), - key_vals::R if event.get_state().intersects(ModifierType::CONTROL_MASK) => { - ui_to_nes_sender.send(UiEvent::Reset).unwrap() - } - key_vals::Escape => { - if paused.load(Ordering::Relaxed) { - ui_to_nes_sender.send(UiEvent::Resume).unwrap(); - paused.store(false, Ordering::Relaxed); - } else { - ui_to_nes_sender.send(UiEvent::Pause).unwrap(); - paused.store(true, Ordering::Relaxed); - } - } - _ => {} - } - - Inhibit(false) - }); - } - - { - window.connect_key_release_event(move |_, event| { - let mut ctrl = ctrl_state.lock().unwrap(); - - // should be fixed with https://github.com/gtk-rs/gdk/pull/358 - let mut val = event.get_keyval(); - *val = keyval_to_upper(*val); - match val { - key_vals::J => ctrl.release(StandardNESKey::B), - key_vals::K => ctrl.release(StandardNESKey::A), - key_vals::U => ctrl.release(StandardNESKey::Select), - key_vals::I => ctrl.release(StandardNESKey::Start), - key_vals::W => ctrl.release(StandardNESKey::Up), - key_vals::S => ctrl.release(StandardNESKey::Down), - key_vals::A => ctrl.release(StandardNESKey::Left), - key_vals::D => ctrl.release(StandardNESKey::Right), - _ => {} - } - - Inhibit(false) - }); - } - - // Support for dragging a new file into the emulator - const DRAG_ID: u32 = 100; - window.drag_dest_set( - DestDefaults::ALL, - &[TargetEntry::new( - "text/plain", - TargetFlags::OTHER_APP, - DRAG_ID, - )], - DragAction::COPY, - ); - - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - window.connect_drag_data_received(move |_, _, _x, _y, data, info, _| { - if info == DRAG_ID { - if let Some(text) = data.get_text() { - let text = text.trim_start_matches("file://"); - - // we don't want to panic and exit, just ignore if corrupted - if text.ends_with(".nes") { - ui_to_nes_sender - .send(UiEvent::LoadRom(text.to_owned())) - .unwrap(); - } - } - } - }); - } - - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - menu_action_reset.connect_activate(move |_| { - ui_to_nes_sender.send(UiEvent::Reset).unwrap(); - }); - } - - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - let paused = self.paused.clone(); - menu_action_pause.connect_activate(move |_| { - ui_to_nes_sender.send(UiEvent::Pause).unwrap(); - paused.store(true, Ordering::Relaxed); - }); - } - - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - let paused = self.paused.clone(); - menu_action_resume.connect_activate(move |_| { - ui_to_nes_sender.send(UiEvent::Resume).unwrap(); - paused.store(false, Ordering::Relaxed); - }); - } - - { - let app = app.clone(); - menu_action_quit.connect_activate(move |_| app.quit()); - } - - { - let ui_to_nes_sender = ui_to_nes_sender.clone(); - menu_action_open.connect_activate(move |_| { - let dialog = FileChooserDialog::with_buttons::( - Some("Select NES ROM"), - None, - FileChooserAction::Open, - &[ - ("_Cancel", ResponseType::Cancel), - ("_Open", ResponseType::Accept), - ], - ); - - let filter = FileFilter::new(); - filter.add_mime_type("application/x-nes-rom"); - dialog.set_filter(&filter); - - let result = dialog.run(); - if result == ResponseType::Accept { - if let Some(file) = dialog.get_filename() { - ui_to_nes_sender - .send(UiEvent::LoadRom(file.to_string_lossy().to_string())) - .unwrap(); - } - } - dialog.close(); - }); - } - - app.connect_activate(move |app| { - app.add_window(&window); - }); - - timeout_add_local(1000 / 120, move || { - if let Ok(event) = nes_to_ui_receiver.try_recv() { - match event { - BackendEvent::PresentStates(states) => { - let mut states_labels = [false; NUMBER_OF_STATES as usize]; - - for i in states.iter() { - if let Some(ptr) = states_labels.get_mut(*i as usize - 1) { - *ptr = true; - } - } - - for (i, &label) in states_labels.iter().enumerate() { - let label = - format!("_{} {}", i + 1, if label { "saved" } else { "" }); - - if let Some(item) = save_state_menu_list.get_children().get(i ) - { - if let Some(item) = item.downcast_ref::() { - item.set_label(&label); - } - } - if let Some(item) = load_state_menu_list.get_children().get(i ) - { - if let Some(item) = item.downcast_ref::() { - item.set_label(&label); - } - } - } - } - } - } - - drawing_area.queue_draw_area( - 0, - 0, - drawing_area.get_allocated_width(), - drawing_area.get_allocated_height(), - ); - glib::Continue(true) - }); - - app.run(&[]); - ui_to_nes_sender.send(UiEvent::Exit).unwrap(); - } -} diff --git a/nes_ui_gtk/ui.glade b/nes_ui_gtk/ui.glade deleted file mode 100644 index 7f375ec..0000000 --- a/nes_ui_gtk/ui.glade +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - False - Plastic - - - True - False - vertical - - - True - False - - - True - False - _File - True - - - True - False - - - True - False - _Open - True - - - - - True - False - - - - - True - False - _Save state - True - - - True - False - - - - - - - True - False - _Load state - True - - - True - False - - - - - - - True - False - - - - - True - False - _Quit - True - - - - - - - - - True - False - _Game - True - - - True - False - - - True - False - _Reset - True - - - - - True - False - _Pause - True - - - - - True - False - _Resume - True - - - - - - - - - False - True - 0 - - - - - True - False - - - True - True - 1 - - - - - - - - - diff --git a/nes_ui_native_windows/Cargo.toml b/nes_ui_native_windows/Cargo.toml deleted file mode 100644 index 47bd132..0000000 --- a/nes_ui_native_windows/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "nes_ui_native_windows" -version = "0.1.0" -authors = ["Amjad Alsharafi "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -# this package as a whole is only for windows -[target.'cfg(windows)'.dependencies] -plastic_core = { path = "../plastic_core" } - -native-windows-gui = "^1.0.3" -native-windows-derive = "^1.0.1" -winapi = "^0.3.9" diff --git a/nes_ui_native_windows/src/main.rs b/nes_ui_native_windows/src/main.rs deleted file mode 100644 index 7905784..0000000 --- a/nes_ui_native_windows/src/main.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![windows_subsystem = "windows"] - -#[cfg(target_os = "windows")] -mod ui; - -#[cfg(target_os = "windows")] -fn main() { - use plastic_core::nes::NES; - use std::env::args; - use ui::NwgProvider; - - let args = args().collect::>(); - - let nes = if args.len() >= 2 { - NES::new(&args[1], NwgProvider::new()) - } else { - Ok(NES::new_without_file(NwgProvider::new())) - }; - - match nes { - Ok(mut nes) => { - nes.run(); - } - Err(err) => { - eprintln!("[ERROR] {}", err); - } - } -} - -#[cfg(not(target_os = "windows"))] -fn main() { - eprintln!("This package can only be compiled to windows"); -} diff --git a/nes_ui_native_windows/src/ui.rs b/nes_ui_native_windows/src/ui.rs deleted file mode 100644 index 00e1f20..0000000 --- a/nes_ui_native_windows/src/ui.rs +++ /dev/null @@ -1,539 +0,0 @@ -use nes_ui_base::{ - nes_controller::{StandardNESControllerState, StandardNESKey}, - nes_display::{Color as NESColor, TV_HEIGHT, TV_WIDTH}, - BackendEvent, UiEvent, UiProvider, -}; -use std::cell::Cell; -use std::sync::{ - atomic::AtomicBool, - atomic::Ordering, - mpsc::{Receiver, Sender}, - Arc, Mutex, -}; - -use native_windows_derive as nwd; -use native_windows_gui as nwg; - -use nwd::NwgUi; -use nwg::{ - keys, ControlHandle, EventData, ExternCanvas, FileDialog, FileDialogAction, Menu, MenuItem, - MenuSeparator, NativeUi, Timer, Window, -}; -use winapi::um::{ - wingdi::{ - BitBlt, CreateBitmap, CreateCompatibleDC, CreateSolidBrush, DeleteDC, DeleteObject, - SelectObject, SetStretchBltMode, StretchBlt, COLORONCOLOR, RGB, SRCCOPY, - }, - winuser::FillRect, -}; - -const NUMBER_OF_STATES: u8 = 10; - -#[derive(NwgUi)] -pub struct ProviderApp { - #[nwg_control( - size: (TV_WIDTH as i32 * 3, TV_HEIGHT as i32 * 3), - title: "Plastic", - flags: "WINDOW|VISIBLE|MAIN_WINDOW|RESIZABLE", - accept_files: true - )] - #[nwg_events( - OnWindowClose: [nwg::stop_thread_dispatch()], - // handle all cases for resizing [not good] :( - OnInit: [ProviderApp::init(SELF, CTRL)], - OnResize: [ProviderApp::window_resize(SELF, CTRL)], - OnWindowMaximize: [ProviderApp::window_resize(SELF, CTRL)], - OnWindowMinimize: [ProviderApp::window_resize(SELF, CTRL)], - - OnKeyPress: [ProviderApp::key_pressed(SELF, EVT_DATA)], - OnKeyRelease: [ProviderApp::key_released(SELF, EVT_DATA)], - - OnFileDrop: [ProviderApp::file_drop(SELF, EVT_DATA)], - )] - window: Window, - - #[nwg_control(parent: Some(&data.window), position: (0, 0), size: (280, 280))] - #[nwg_events(OnPaint: [ProviderApp::paint(SELF, CTRL, EVT_DATA)])] - canvas: ExternCanvas, - - #[nwg_control(parent: window, interval: 1000/60, stopped: false)] - #[nwg_events(OnTimerTick: [ProviderApp::timer_tick(SELF)])] - timer: Timer, - - #[nwg_control(parent: window, text: "&File", disabled: false, popup: false)] - file_menu: Menu, - - #[nwg_control(parent: file_menu, text: "&Open", disabled: false, check: false)] - #[nwg_events(OnMenuItemSelected: [ProviderApp::menu_action_open(SELF)])] - file_menu_open_action: MenuItem, - - #[nwg_control(parent: file_menu)] - _menu_separator_1: MenuSeparator, - - #[nwg_control(parent: file_menu, text: "&Save state", disabled: false, popup: false)] - file_menu_save_state_menu: Menu, - - #[nwg_control(parent: file_menu_save_state_menu, text: "&1 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_1: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&2 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_2: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&3 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_3: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&4 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_4: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&5 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_5: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&6 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_6: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&7 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_7: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&8 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_8: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&9 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_9: MenuItem, - #[nwg_control(parent: file_menu_save_state_menu, text: "&10 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::save_state(SELF, HANDLE)])] - save_item_10: MenuItem, - - #[nwg_control(parent: file_menu, text: "&Load state", disabled: false, popup: false)] - file_menu_load_state_menu: Menu, - - #[nwg_control(parent: file_menu)] - _menu_separator_2: MenuSeparator, - - #[nwg_control(parent: file_menu_load_state_menu, text: "&1 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_1: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&2 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_2: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&3 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_3: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&4 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_4: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&5 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_5: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&6 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_6: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&7 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_7: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&8 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_8: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&9 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_9: MenuItem, - #[nwg_control(parent: file_menu_load_state_menu, text: "&10 ")] - #[nwg_events(OnMenuItemSelected: [ProviderApp::load_state(SELF, HANDLE)])] - load_item_10: MenuItem, - - #[nwg_control(parent: file_menu, text: "&Quit", disabled: false, check: false)] - #[nwg_events(OnMenuItemSelected: [ProviderApp::menu_action_quit(SELF)])] - file_menu_quit_action: MenuItem, - - #[nwg_control(parent: window, text: "&Game", disabled: false, popup: false)] - game_menu: Menu, - - #[nwg_control(parent: game_menu, text: "&Reset", disabled: false, check: false)] - #[nwg_events(OnMenuItemSelected: [ProviderApp::menu_action_reset(SELF)])] - game_menu_reset_action: MenuItem, - - #[nwg_control(parent: game_menu, text: "&Pause", disabled: false, check: false)] - #[nwg_events(OnMenuItemSelected: [ProviderApp::menu_action_pause(SELF)])] - game_menu_pause_action: MenuItem, - - #[nwg_control(parent: game_menu, text: "&Resume", disabled: false, check: false)] - #[nwg_events(OnMenuItemSelected: [ProviderApp::menu_action_resume(SELF)])] - game_menu_resume_action: MenuItem, - - ui_to_nes_sender: Sender, - nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - - paused: Arc, - - /// flag to indicate that the windows has been resized and we should clear - /// the canvas, Fixes flickering - need_clear_rect: Cell, -} - -impl ProviderApp { - fn initial_state( - ui_to_nes_sender: Sender, - nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - ) -> Self { - Self { - window: Default::default(), - canvas: Default::default(), - timer: Default::default(), - file_menu: Default::default(), - file_menu_open_action: Default::default(), - _menu_separator_1: Default::default(), - file_menu_save_state_menu: Default::default(), - save_item_1: Default::default(), - save_item_2: Default::default(), - save_item_3: Default::default(), - save_item_4: Default::default(), - save_item_5: Default::default(), - save_item_6: Default::default(), - save_item_7: Default::default(), - save_item_8: Default::default(), - save_item_9: Default::default(), - save_item_10: Default::default(), - file_menu_load_state_menu: Default::default(), - _menu_separator_2: Default::default(), - load_item_1: Default::default(), - load_item_2: Default::default(), - load_item_3: Default::default(), - load_item_4: Default::default(), - load_item_5: Default::default(), - load_item_6: Default::default(), - load_item_7: Default::default(), - load_item_8: Default::default(), - load_item_9: Default::default(), - load_item_10: Default::default(), - file_menu_quit_action: Default::default(), - game_menu: Default::default(), - game_menu_reset_action: Default::default(), - game_menu_pause_action: Default::default(), - game_menu_resume_action: Default::default(), - - ui_to_nes_sender, - nes_to_ui_receiver, - image, - ctrl_state, - paused: Arc::new(AtomicBool::new(false)), - - need_clear_rect: Cell::new(true), - } - } -} - -impl ProviderApp { - fn init(&self, ctrl: &Window) { - self.window_resize(ctrl); - } - - fn get_menu_position(handle: &ControlHandle) -> u32 { - use winapi::um::winuser::GetMenuItemID; - - if let Some((hmenu, id)) = handle.hmenu_item() { - let position_0_id = unsafe { GetMenuItemID(hmenu, 0) }; - - id - position_0_id - } else { - 0 - } - } - - fn save_state(&self, handle: &ControlHandle) { - self.ui_to_nes_sender - .send(UiEvent::SaveState( - Self::get_menu_position(handle) as u8 + 1, - )) - .unwrap(); - } - - fn load_state(&self, handle: &ControlHandle) { - self.ui_to_nes_sender - .send(UiEvent::LoadState( - Self::get_menu_position(handle) as u8 + 1, - )) - .unwrap(); - } - - fn window_resize(&self, ctrl: &Window) { - self.canvas.set_size(ctrl.size().0, ctrl.size().1); - - // mark as dirty, and need to clear the canvas - self.need_clear_rect.set(true); - } - - fn key_pressed(&self, data: &EventData) { - let mut ctrl = self.ctrl_state.lock().unwrap(); - - match data.on_key() { - keys::_J => ctrl.press(StandardNESKey::B), - keys::_K => ctrl.press(StandardNESKey::A), - keys::_U => ctrl.press(StandardNESKey::Select), - keys::_I => ctrl.press(StandardNESKey::Start), - keys::_W => ctrl.press(StandardNESKey::Up), - keys::_S => ctrl.press(StandardNESKey::Down), - keys::_A => ctrl.press(StandardNESKey::Left), - keys::_D => ctrl.press(StandardNESKey::Right), - keys::ESCAPE => { - if self.paused.load(Ordering::Relaxed) { - self.ui_to_nes_sender.send(UiEvent::Resume).unwrap(); - self.paused.store(false, Ordering::Relaxed); - } else { - self.ui_to_nes_sender.send(UiEvent::Pause).unwrap(); - self.paused.store(true, Ordering::Relaxed); - } - } - _ => {} - } - } - - fn key_released(&self, data: &EventData) { - let mut ctrl = self.ctrl_state.lock().unwrap(); - - match data.on_key() { - keys::_J => ctrl.release(StandardNESKey::B), - keys::_K => ctrl.release(StandardNESKey::A), - keys::_U => ctrl.release(StandardNESKey::Select), - keys::_I => ctrl.release(StandardNESKey::Start), - keys::_W => ctrl.release(StandardNESKey::Up), - keys::_S => ctrl.release(StandardNESKey::Down), - keys::_A => ctrl.release(StandardNESKey::Left), - keys::_D => ctrl.release(StandardNESKey::Right), - _ => {} - } - } - - fn file_drop(&self, data: &EventData) { - let drop_files = data.on_file_drop(); - - if drop_files.len() > 0 { - if let Some(filename) = drop_files.files().iter().find(|e| e.ends_with(".nes")) { - self.ui_to_nes_sender - .send(UiEvent::LoadRom(filename.to_string())) - .unwrap(); - } - } - } - - fn menu_action_open(&self) { - let mut file_dialog = Default::default(); - - FileDialog::builder() - .title("Select NES ROM") - .action(FileDialogAction::Open) - .multiselect(false) - .filters("NES ROM(*.nes)") - .build(&mut file_dialog) - .unwrap(); - - if file_dialog.run(Some(&self.window)) { - if let Ok(filename) = file_dialog.get_selected_item() { - if filename.ends_with(".nes") { - self.ui_to_nes_sender - .send(UiEvent::LoadRom(filename)) - .unwrap(); - } - } - } - } - - fn menu_action_quit(&self) { - nwg::stop_thread_dispatch(); - } - - fn menu_action_reset(&self) { - self.ui_to_nes_sender.send(UiEvent::Reset).unwrap() - } - - fn menu_action_pause(&self) { - self.ui_to_nes_sender.send(UiEvent::Pause).unwrap(); - self.paused.store(true, Ordering::Relaxed); - } - - fn menu_action_resume(&self) { - self.ui_to_nes_sender.send(UiEvent::Resume).unwrap(); - self.paused.store(false, Ordering::Relaxed); - } - - fn paint(&self, ctrl: &ExternCanvas, data: &EventData) { - let paint = data.on_paint(); - let ps = paint.begin_paint(); - - let hdc = ps.hdc; - let rc = &ps.rcPaint; - - let data: *const u8 = self.image.lock().unwrap().as_ptr(); - - // All/Most functions from the winapi are unsafe, so ya - unsafe { - // clear only if needed, clearing every time produce flickering effect - if self.need_clear_rect.get() { - let brush: *mut _ = &mut CreateSolidBrush(RGB(0, 0, 0)); - FillRect(hdc, rc, brush as _); - self.need_clear_rect.set(false); - } - - let bitmap = CreateBitmap(TV_WIDTH as i32, TV_HEIGHT as i32, 1, 32, data as _); - - // used for setting the bitmap and scaling it before moving it to - // the original hdc - let hdctmp = CreateCompatibleDC(hdc); - - let hbmold = SelectObject(hdctmp, bitmap as _); - - BitBlt( - hdctmp, - 0, - 0, - TV_WIDTH as i32, - TV_HEIGHT as i32, - hdctmp, - 0, - 0, - SRCCOPY, - ); - - SetStretchBltMode(hdctmp, COLORONCOLOR); - - let area_width = ctrl.size().0 as f64; - let area_height = ctrl.size().1 as f64; - - // Got from the GTK UI code - let scale_width = area_width / TV_WIDTH as f64; - let scale_height = area_height / TV_HEIGHT as f64; - - let scale_smallest; - let mut top = 0.; - let mut left = 0.; - - if scale_width > scale_height { - scale_smallest = scale_height; - left = (area_width - (TV_WIDTH as f64 * scale_smallest)) / 2.; - } else { - scale_smallest = scale_width; - top = (area_height - (TV_HEIGHT as f64 * scale_smallest)) / 2.; - }; - - let new_width = (TV_WIDTH as f64 * scale_smallest) as i32; - let new_height = (TV_HEIGHT as f64 * scale_smallest) as i32; - - StretchBlt( - hdc, - left as i32, - top as i32, - new_width, - new_height, - hdctmp, - 0, - 0, - TV_WIDTH as i32, - TV_HEIGHT as i32, - SRCCOPY, - ); - - SelectObject(hdctmp, hbmold); - DeleteDC(hdctmp); - DeleteObject(bitmap as _); - } - - paint.end_paint(&ps); - } - - fn timer_tick(&self) { - if let Ok(event) = self.nes_to_ui_receiver.try_recv() { - match event { - BackendEvent::PresentStates(states) => { - let mut states_labels = [false; NUMBER_OF_STATES as usize]; - - for i in states.iter() { - if let Some(ptr) = states_labels.get_mut(*i as usize - 1) { - *ptr = true; - } - } - - let save_menu_ctrl: ControlHandle = (&self.file_menu_save_state_menu).into(); - let load_menu_ctrl: ControlHandle = (&self.file_menu_load_state_menu).into(); - - if let Some((_s_hmenu_parent, save_hmenu)) = save_menu_ctrl.hmenu() { - if let Some((_l_hmenu_parent, load_hmenu)) = load_menu_ctrl.hmenu() { - use winapi::um::winuser::{GetMenuItemID, ModifyMenuA}; - use winapi::um::winuser::{MF_BYPOSITION, MF_STRING}; - - for (i, &label) in states_labels.iter().enumerate() { - let label = format!( - "&{} {}\0", - i + 1, - if label { "saved" } else { "" } - ); - - // FIXME: add SAFETY argument (windows API) - unsafe { - let id = GetMenuItemID(save_hmenu, i as i32); - - ModifyMenuA( - save_hmenu, - i as u32, - MF_STRING | MF_BYPOSITION, - id as usize, - label.as_ptr() as _, - ); - - let id = GetMenuItemID(load_hmenu, i as i32); - - ModifyMenuA( - load_hmenu, - i as u32, - MF_STRING | MF_BYPOSITION, - id as usize, - label.as_ptr() as _, - ); - } - } - } - } - } - } - } - - self.canvas.invalidate(); - } -} - -pub struct NwgProvider {} - -impl NwgProvider { - pub fn new() -> Self { - Self {} - } -} - -impl UiProvider for NwgProvider { - fn get_tv_color_converter() -> fn(&NESColor) -> [u8; 4] { - |color| [color.b, color.g, color.r, 0xFF] - } - - fn run_ui_loop( - &mut self, - ui_to_nes_sender: Sender, - nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - ) { - nwg::init().expect("Failed to init Native Windows GUI"); - nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font"); - - let _app = ProviderApp::build_ui(ProviderApp::initial_state( - ui_to_nes_sender, - nes_to_ui_receiver, - image, - ctrl_state, - )) - .expect("Failed to build UI"); - - nwg::dispatch_thread_events(); - } -} diff --git a/nes_ui_sfml/src/main.rs b/nes_ui_sfml/src/main.rs deleted file mode 100644 index 7bc6bcb..0000000 --- a/nes_ui_sfml/src/main.rs +++ /dev/null @@ -1,22 +0,0 @@ -mod ui; -use plastic_core::nes::NES; -use std::env::args; -use ui::SfmlProvider; - -fn main() { - let args = args().collect::>(); - - if args.len() < 2 { - eprintln!("USAGE: {} ", args[0]); - return; - } - - match NES::new(&args[1], SfmlProvider {}) { - Ok(mut nes) => { - nes.run(); - } - Err(err) => { - eprintln!("[ERROR] {}", err); - } - } -} diff --git a/nes_ui_sfml/src/ui.rs b/nes_ui_sfml/src/ui.rs deleted file mode 100644 index 60b162f..0000000 --- a/nes_ui_sfml/src/ui.rs +++ /dev/null @@ -1,193 +0,0 @@ -use plastic_core::{ - nes_controller::{StandardNESControllerState, StandardNESKey}, - nes_display::{Color as NESColor, TV_HEIGHT, TV_WIDTH}, - BackendEvent, UiEvent, UiProvider, -}; - -use std::sync::{ - mpsc::{Receiver, Sender}, - Arc, Mutex, -}; - -use sfml::{ - graphics::{Color, FloatRect, Image, RenderTarget, RenderWindow, Sprite, Texture, View}, - system::{SfBox, Vector2f}, - window::{joystick::Axis, Event, Key, Style}, -}; - -pub struct SfmlProvider {} - -impl SfmlProvider { - /// calculate a new view based on the window size - fn get_view( - window_width: u32, - window_height: u32, - target_width: u32, - target_height: u32, - ) -> SfBox { - let mut viewport = FloatRect::new(0., 0., 1., 1.); - - let screen_width = window_width as f32 / target_width as f32; - let screen_height = window_height as f32 / target_height as f32; - - if screen_width > screen_height { - viewport.width = screen_height / screen_width; - viewport.left = (1. - viewport.width) / 2.; - } else if screen_height > screen_width { - viewport.height = screen_width / screen_height; - viewport.top = (1. - viewport.height) / 2.; - } - - let mut view = View::new( - Vector2f::new((TV_WIDTH / 2) as f32, (TV_HEIGHT / 2) as f32), - Vector2f::new((TV_WIDTH) as f32, (TV_HEIGHT) as f32), - ); - - view.set_viewport(&viewport); - - view - } -} -impl UiProvider for SfmlProvider { - fn run_ui_loop( - &mut self, - ui_to_nes_sender: Sender, - _nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - ) { - let mut window = RenderWindow::new( - (TV_WIDTH as u32 * 3, TV_HEIGHT as u32 * 3), - "Plastic", - Style::CLOSE | Style::RESIZE, - &Default::default(), - ); - window.set_vertical_sync_enabled(true); - window.set_framerate_limit(60); - - // to scale the view into the window - // this view is in the size of the NES TV - // but we can scale the window and all the pixels will be scaled - // accordingly - window.set_view(&Self::get_view( - window.size().x, - window.size().y, - TV_WIDTH as u32, - TV_HEIGHT as u32, - )); - - let mut texture = Texture::new(TV_WIDTH as u32, TV_HEIGHT as u32).expect("texture"); - - 'main: loop { - if let Ok(mut ctrl) = ctrl_state.lock() { - while let Some(event) = window.poll_event() { - match event { - Event::Closed => break 'main, - Event::Resized { width, height } => { - window.set_view(&Self::get_view( - width, - height, - TV_WIDTH as u32, - TV_HEIGHT as u32, - )); - } - Event::KeyPressed { - code: key, - ctrl: ctrl_key, - .. - } => match key { - Key::J => ctrl.press(StandardNESKey::B), - Key::K => ctrl.press(StandardNESKey::A), - Key::U => ctrl.press(StandardNESKey::Select), - Key::I => ctrl.press(StandardNESKey::Start), - Key::W => ctrl.press(StandardNESKey::Up), - Key::S => ctrl.press(StandardNESKey::Down), - Key::A => ctrl.press(StandardNESKey::Left), - Key::D => ctrl.press(StandardNESKey::Right), - Key::R if ctrl_key => ui_to_nes_sender.send(UiEvent::Reset).unwrap(), - _ => {} - }, - Event::KeyReleased { code: key, .. } => match key { - Key::J => ctrl.release(StandardNESKey::B), - Key::K => ctrl.release(StandardNESKey::A), - Key::U => ctrl.release(StandardNESKey::Select), - Key::I => ctrl.release(StandardNESKey::Start), - Key::W => ctrl.release(StandardNESKey::Up), - Key::S => ctrl.release(StandardNESKey::Down), - Key::A => ctrl.release(StandardNESKey::Left), - Key::D => ctrl.release(StandardNESKey::Right), - _ => {} - }, - Event::JoystickButtonPressed { - joystickid: 0, - button, - } => match button { - 0 => ctrl.press(StandardNESKey::B), - 1 => ctrl.press(StandardNESKey::A), - 8 => ctrl.press(StandardNESKey::Select), - 9 => ctrl.press(StandardNESKey::Start), - _ => {} - }, - Event::JoystickButtonReleased { - joystickid: 0, - button, - } => match button { - 0 => ctrl.release(StandardNESKey::B), - 1 => ctrl.release(StandardNESKey::A), - 8 => ctrl.release(StandardNESKey::Select), - 9 => ctrl.release(StandardNESKey::Start), - _ => {} - }, - Event::JoystickMoved { - joystickid: 0, - axis, - position, - } => match axis { - Axis::PovY => { - if position > 0. { - ctrl.press(StandardNESKey::Down) - } else if position < 0. { - ctrl.press(StandardNESKey::Up) - } else { - ctrl.release(StandardNESKey::Down); - ctrl.release(StandardNESKey::Up); - } - } - Axis::PovX => { - if position > 0. { - ctrl.press(StandardNESKey::Right) - } else if position < 0. { - ctrl.press(StandardNESKey::Left) - } else { - ctrl.release(StandardNESKey::Right); - ctrl.release(StandardNESKey::Left); - } - } - _ => {} - }, - _ => {} - } - } - } - - window.clear(Color::BLACK); - - { - let pixels = &image.lock().unwrap(); - - let image = Image::create_from_pixels(TV_WIDTH as u32, TV_HEIGHT as u32, pixels) - .expect("image"); - - texture.update_from_image(&image, 0, 0); - } - - window.draw(&Sprite::with_texture(&texture)); - - window.display(); - } - } - - fn get_tv_color_converter() -> fn(&NESColor) -> [u8; 4] { - |color| [color.r, color.g, color.b, 0xFF] - } -} diff --git a/nes_ui_tui/Cargo.toml b/nes_ui_tui/Cargo.toml deleted file mode 100644 index 93e3c78..0000000 --- a/nes_ui_tui/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "nes_ui_tui" -version = "0.1.0" -authors = ["Amjad Alsharafi "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -plastic_core = { path = "../plastic_core" } - -crossterm = "^0.17" -tui = { git = "https://github.com/fdehau/tui-rs", default-features = false, features = ['crossterm'] } -gilrs = "^0.7.4" diff --git a/nes_ui_tui/src/event.rs b/nes_ui_tui/src/event.rs deleted file mode 100644 index 39c35ab..0000000 --- a/nes_ui_tui/src/event.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc, Arc, -}; -use std::thread; -use std::time::Duration; - -use crossterm::{ - event::{poll, read, Event as crosstermEvent, KeyEvent}, - terminal::{disable_raw_mode, enable_raw_mode}, -}; - -pub enum Event { - Input(I), - Tick, -} -/// A small event handler that wrap termion input and tick events. Each event -/// type is handled in its own thread and returned to a common `Receiver` -pub struct Events { - rx: mpsc::Receiver>, - stopped: Arc, -} - -impl Events { - pub fn new(tick_rate: Duration) -> Events { - let (tx, rx) = mpsc::channel(); - let stopped = Arc::new(AtomicBool::new(false)); - - enable_raw_mode().unwrap(); - - { - let tx = tx.clone(); - thread::spawn(move || loop { - if poll(Duration::from_millis(10)).is_ok() { - if let Ok(crosstermEvent::Key(key)) = read() { - if tx.send(Event::Input(key)).is_err() { - return; - } - } - } - }) - }; - - { - let stopped = stopped.clone(); - thread::spawn(move || loop { - if !stopped.load(Ordering::Relaxed) && tx.send(Event::Tick).is_err() { - break; - } - thread::sleep(tick_rate); - }) - }; - Events { rx, stopped } - } - - pub fn set_stopped_state(&self, state: bool) { - self.stopped.store(state, Ordering::Relaxed); - } - - pub fn next(&self) -> Result, mpsc::TryRecvError> { - self.rx.try_recv() - } -} - -impl Drop for Events { - fn drop(&mut self) { - disable_raw_mode().unwrap(); - } -} diff --git a/nes_ui_tui/src/main.rs b/nes_ui_tui/src/main.rs deleted file mode 100644 index d65da81..0000000 --- a/nes_ui_tui/src/main.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod event; -mod ui; -use plastic_core::nes::NES; -use std::env::args; -use ui::TuiProvider; - -fn main() { - let args = args().collect::>(); - - if args.len() < 2 { - eprintln!("USAGE: {} ", args[0]); - return; - } - - match NES::new(&args[1], TuiProvider {}) { - Ok(mut nes) => { - nes.run(); - } - Err(err) => { - eprintln!("[ERROR] {}", err); - } - } -} diff --git a/nes_ui_tui/src/ui.rs b/nes_ui_tui/src/ui.rs deleted file mode 100644 index c9a8518..0000000 --- a/nes_ui_tui/src/ui.rs +++ /dev/null @@ -1,198 +0,0 @@ -use plastic_core::{ - nes_controller::{StandardNESControllerState, StandardNESKey}, - nes_display::{Color as NESColor, TV_HEIGHT, TV_WIDTH}, - BackendEvent, UiEvent, UiProvider, -}; -use std::collections::HashSet; -use std::io; -use std::sync::{ - mpsc::{Receiver, Sender}, - Arc, Mutex, -}; -use std::thread; -use std::time::Duration; - -use crate::event::{Event as tuiEvent, Events}; - -use gilrs::{Button, Event, EventType, Gilrs}; - -use crossterm::event::{KeyCode, KeyModifiers}; -use tui::{ - backend::CrosstermBackend, - style::Color, - symbols::Marker, - widgets::{ - canvas::{Canvas, Shape}, - Block, Borders, - }, - Terminal, -}; - -struct ImageView { - image: Arc>>, -} - -impl Shape for ImageView { - fn draw(&self, painter: &mut tui::widgets::canvas::Painter) { - let data = self.image.lock().unwrap().to_vec(); - - for x in 0..TV_WIDTH { - for y in 0..TV_HEIGHT { - let index = (TV_HEIGHT - y - 1) * TV_WIDTH + x; - if let Some((x, y)) = painter.get_point(x as f64, y as f64) { - let pixel = data.get(index * 4..(index + 1) * 4).unwrap(); - painter.paint(x, y, Color::Rgb(pixel[0], pixel[1], pixel[2])); - } - } - } - } -} - -pub struct TuiProvider {} - -impl UiProvider for TuiProvider { - fn get_tv_color_converter() -> fn(&NESColor) -> [u8; 4] { - |color| [color.r, color.g, color.b, 0xFF] - } - - fn run_ui_loop( - &mut self, - ui_to_nes_sender: Sender, - _nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - ) { - let stdout = io::stdout(); - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); - - let mut gilrs = Gilrs::new().unwrap(); - - let mut active_gamepad = None; - - let keyboard_events = Events::new(Duration::from_millis(1000 / 30)); - - // FIXME: find better way to handle input - let mut not_pressed = HashSet::new(); - let mut pressed = HashSet::new(); - not_pressed.insert(StandardNESKey::A); - not_pressed.insert(StandardNESKey::B); - not_pressed.insert(StandardNESKey::Select); - not_pressed.insert(StandardNESKey::Start); - not_pressed.insert(StandardNESKey::Up); - not_pressed.insert(StandardNESKey::Down); - not_pressed.insert(StandardNESKey::Left); - not_pressed.insert(StandardNESKey::Right); - - 'outer: loop { - let image = image.clone(); - terminal - .draw(move |f| { - let canvas = Canvas::default() - .block(Block::default().borders(Borders::ALL).title("Plastic")) - .x_bounds([0., TV_WIDTH as f64]) - .y_bounds([0., TV_HEIGHT as f64]) - .marker(Marker::Dot) - .paint(|ctx| { - ctx.draw(&ImageView { - image: image.clone(), - }); - }); - f.render_widget(canvas, f.size()); - }) - .unwrap(); - - if let Ok(mut ctrl) = ctrl_state.lock() { - while let Ok(event) = keyboard_events.next() { - match event { - tuiEvent::Input(input) => { - let modifiers = input.modifiers; - let input = input.code; - let possible_button = match input { - KeyCode::Esc => break 'outer, - KeyCode::Char('R') | KeyCode::Char('r') - if modifiers.intersects(KeyModifiers::CONTROL) => - { - ui_to_nes_sender.send(UiEvent::Reset).unwrap(); - None - } - KeyCode::Char('J') | KeyCode::Char('j') => Some(StandardNESKey::B), - KeyCode::Char('K') | KeyCode::Char('k') => Some(StandardNESKey::A), - KeyCode::Char('U') | KeyCode::Char('u') => { - Some(StandardNESKey::Select) - } - KeyCode::Char('I') | KeyCode::Char('i') => { - Some(StandardNESKey::Start) - } - KeyCode::Char('W') | KeyCode::Char('w') => Some(StandardNESKey::Up), - KeyCode::Char('S') | KeyCode::Char('s') => { - Some(StandardNESKey::Down) - } - KeyCode::Char('A') | KeyCode::Char('a') => { - Some(StandardNESKey::Left) - } - KeyCode::Char('D') | KeyCode::Char('d') => { - Some(StandardNESKey::Right) - } - _ => None, - }; - if let Some(button) = possible_button { - not_pressed.remove(&button); - pressed.insert(button); - } - } - tuiEvent::Tick => { - for button in ¬_pressed { - ctrl.release(*button); - } - for button in &pressed { - ctrl.press(*button); - } - - not_pressed.insert(StandardNESKey::A); - not_pressed.insert(StandardNESKey::B); - not_pressed.insert(StandardNESKey::Select); - not_pressed.insert(StandardNESKey::Start); - not_pressed.insert(StandardNESKey::Up); - not_pressed.insert(StandardNESKey::Down); - not_pressed.insert(StandardNESKey::Left); - not_pressed.insert(StandardNESKey::Right); - - pressed.clear(); - } - } - } - - // set events in the cache and check if gamepad is still active - while let Some(Event { id, event, .. }) = gilrs.next_event() { - active_gamepad = Some(id); - if event == EventType::Disconnected { - keyboard_events.set_stopped_state(false); - active_gamepad = None; - } - } - keyboard_events.set_stopped_state(active_gamepad.is_some()); - - if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) { - for (controller_button, nes_button) in &[ - (Button::South, StandardNESKey::B), - (Button::East, StandardNESKey::A), - (Button::Select, StandardNESKey::Select), - (Button::Start, StandardNESKey::Start), - (Button::DPadUp, StandardNESKey::Up), - (Button::DPadDown, StandardNESKey::Down), - (Button::DPadRight, StandardNESKey::Right), - (Button::DPadLeft, StandardNESKey::Left), - ] { - if gamepad.is_pressed(*controller_button) { - ctrl.press(*nes_button); - } else { - ctrl.release(*nes_button); - } - } - } - } - thread::sleep(Duration::from_millis(10)); - } - } -} diff --git a/plastic_core/Cargo.toml b/plastic_core/Cargo.toml index 6e02eb3..16cbf4c 100644 --- a/plastic_core/Cargo.toml +++ b/plastic_core/Cargo.toml @@ -7,12 +7,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -directories-next = "2.0.0" -regex = "1.3.9" bitflags = "^1.2.1" serde = { version = "1.0.115", features = ["derive"] } bincode = "1.3.1" -# used by the apu only -rodio = { version = "^0.11.0", default-features = false, features = [] } diff --git a/plastic_core/src/apu2a03/mod.rs b/plastic_core/src/apu2a03/mod.rs index 4e68bca..f4cc347 100644 --- a/plastic_core/src/apu2a03/mod.rs +++ b/plastic_core/src/apu2a03/mod.rs @@ -17,12 +17,10 @@ use length_counter::LengthCountedChannel; use serde::{Deserialize, Serialize}; use std::cell::Cell; use std::sync::{Arc, Mutex}; -use tone_source::{APUChannel, APUChannelPlayer, BufferedChannel, TimedAPUChannel}; - -use rodio::DeviceTrait; +use tone_source::{APUChannel, BufferedChannel, TimedAPUChannel}; // for performance -pub const SAMPLE_RATE: u32 = 22050; +pub const SAMPLE_RATE: u32 = 44100; // after how many apu clocks a sample should be recorded // APU, is clocked on every CPU clock @@ -80,9 +78,6 @@ pub struct APU2A03 { interrupt_flag: Cell, request_interrupt_flag_change: Cell, - - #[serde(skip)] - player: Option, } impl APU2A03 { @@ -115,31 +110,6 @@ impl APU2A03 { interrupt_flag: Cell::new(false), request_interrupt_flag_change: Cell::new(false), - - player: Self::get_player(buffered_channel), - } - } - - fn get_player(channel: Arc>) -> Option { - let device = rodio::default_output_device()?; - - // bug in rodio, that it panics if the device does not support any format - // it is fixed now in github, not sure when is the release coming - let formats = device.supported_output_formats().ok()?; - if formats.count() > 0 { - let sink = rodio::Sink::new(&device); - - let low_pass_player = - rodio::source::Source::low_pass(APUChannelPlayer::from_clone(channel), 10000); - - sink.append(low_pass_player); - sink.set_volume(0.15); - - sink.pause(); - - Some(sink) - } else { - None } } @@ -418,18 +388,6 @@ impl APU2A03 { } } - pub fn play(&self) { - if let Some(ref player) = self.player { - player.play(); - } - } - - pub fn pause(&self) { - if let Some(ref player) = self.player { - player.pause(); - } - } - fn generate_quarter_frame_clock(&mut self) { self.square_pulse_1.clock_envlope(); self.square_pulse_2.clock_envlope(); @@ -475,12 +433,6 @@ impl APU2A03 { pulse_out + tnd_out } - pub fn empty_queue(&mut self) { - if let Ok(mut buffer) = self.buffered_channel.lock() { - buffer.clear_buffer(); - } - } - /// clock the APU **at** CPU clock rate, the clocks are handled correctly /// as it should be pub fn clock(&mut self) { @@ -576,6 +528,10 @@ impl APU2A03 { } } } + + pub fn take_audio_buffer(&self) -> Vec { + self.buffered_channel.lock().unwrap().take_buffer() + } } impl CPUIrqProvider for APU2A03 { @@ -632,8 +588,6 @@ impl Savable for APU2A03 { let _ = std::mem::replace(self, state); - self.player = Self::get_player(self.buffered_channel.clone()); - Ok(()) } } diff --git a/plastic_core/src/apu2a03/tone_source.rs b/plastic_core/src/apu2a03/tone_source.rs index a21ba37..e3c522d 100644 --- a/plastic_core/src/apu2a03/tone_source.rs +++ b/plastic_core/src/apu2a03/tone_source.rs @@ -1,7 +1,5 @@ -use rodio::Source; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; -use std::sync::{Arc, Mutex}; pub trait APUChannel { fn get_output(&mut self) -> f32; @@ -64,8 +62,8 @@ impl BufferedChannel { } } - pub fn clear_buffer(&mut self) { - self.buffer.clear(); + pub fn take_buffer(&mut self) -> Vec { + self.buffer.drain(..).collect() } } @@ -88,53 +86,3 @@ impl APUChannel for BufferedChannel { } } } - -pub struct APUChannelPlayer -where - S: APUChannel, -{ - source: Arc>, -} - -impl APUChannelPlayer -where - S: APUChannel, -{ - pub fn from_clone(source: Arc>) -> Self { - Self { source } - } -} - -impl Iterator for APUChannelPlayer -where - S: APUChannel, -{ - type Item = f32; - fn next(&mut self) -> Option { - Some(self.source.lock().unwrap().get_output()) - } -} - -impl Source for APUChannelPlayer -where - S: APUChannel, -{ - #[inline] - fn current_frame_len(&self) -> Option { - None - } - - #[inline] - fn channels(&self) -> u16 { - 1 - } - - #[inline] - fn sample_rate(&self) -> u32 { - super::SAMPLE_RATE - } - - fn total_duration(&self) -> Option { - None - } -} diff --git a/plastic_core/src/cartridge/mappers/tests.rs b/plastic_core/src/cartridge/mappers/tests.rs index 8ca7e34..5c31b62 100644 --- a/plastic_core/src/cartridge/mappers/tests.rs +++ b/plastic_core/src/cartridge/mappers/tests.rs @@ -8,7 +8,6 @@ mod mappers_tests { /// WRAM, PRG ROM, IRQ, and CHR ROM/RAM. fn run_holy_mapperel_test(filename: &str, mapper_id: u8) -> Result<(), TestError> { let mut nes = NesTester::new(filename)?; - nes.reset_cpu(); // cannot use until infinite loop :( nes.clock_until_pixel_appears(194, 65, 0x38); diff --git a/plastic_core/src/common/save_state.rs b/plastic_core/src/common/save_state.rs index 69748ba..140d099 100644 --- a/plastic_core/src/common/save_state.rs +++ b/plastic_core/src/common/save_state.rs @@ -11,6 +11,7 @@ pub trait Savable { #[derive(Debug)] pub enum SaveError { IoError(ioError), + ContainExtraData, Others, } @@ -26,6 +27,9 @@ impl Display for SaveError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SaveError::IoError(err) => write!(f, "IO Error: {}", err), + SaveError::ContainExtraData => { + write!(f, "Contain Extra Data after the end of the file") + } SaveError::Others => write!(f, "Others"), } } diff --git a/plastic_core/src/controller/mod.rs b/plastic_core/src/controller/mod.rs index 47ebf3e..fb92aeb 100644 --- a/plastic_core/src/controller/mod.rs +++ b/plastic_core/src/controller/mod.rs @@ -1,7 +1,6 @@ use crate::common::{Bus, Device}; use bitflags::bitflags; use std::cell::Cell; -use std::sync::{Arc, Mutex}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum StandardNESKey { @@ -29,17 +28,25 @@ bitflags! { } impl StandardNESControllerState { - pub fn press(&mut self, key: StandardNESKey) { + fn press(&mut self, key: StandardNESKey) { self.insert(StandardNESControllerState::from_bits(key as u8).unwrap()); } - pub fn release(&mut self, key: StandardNESKey) { + fn release(&mut self, key: StandardNESKey) { self.remove(StandardNESControllerState::from_bits(key as u8).unwrap()); } + + pub fn set_state(&mut self, key: StandardNESKey, pressed: bool) { + if pressed { + self.press(key); + } else { + self.release(key); + } + } } pub struct Controller { - primary_state: Arc>, + primary_state: StandardNESControllerState, polled_state: Cell, polling: bool, @@ -48,15 +55,15 @@ pub struct Controller { impl Controller { pub fn new() -> Self { Self { - primary_state: Arc::new(Mutex::new(StandardNESControllerState::empty())), + primary_state: StandardNESControllerState::empty(), polled_state: Cell::new(0), polling: false, } } - pub fn get_primary_controller_state(&self) -> Arc> { - self.primary_state.clone() + pub fn set_state(&mut self, key: StandardNESKey, pressed: bool) { + self.primary_state.set_state(key, pressed); } } @@ -64,9 +71,7 @@ impl Bus for Controller { fn read(&self, _address: u16, _device: Device) -> u8 { // refresh polled here if self.polling { - if let Ok(primary_state) = self.primary_state.lock() { - self.polled_state.set(primary_state.bits); - } + self.polled_state.set(self.primary_state.bits); } let result = self.polled_state.get() & 1; @@ -80,9 +85,7 @@ impl Bus for Controller { // if the state changed, then refresh if self.polling ^ new_polling { - if let Ok(primary_state) = self.primary_state.lock() { - self.polled_state.set(primary_state.bits); - } + self.polled_state.set(self.primary_state.bits); } self.polling = new_polling; diff --git a/plastic_core/src/cpu6502/mod.rs b/plastic_core/src/cpu6502/mod.rs index 838c3e7..1a2e009 100644 --- a/plastic_core/src/cpu6502/mod.rs +++ b/plastic_core/src/cpu6502/mod.rs @@ -130,11 +130,6 @@ where self.bus.reset() } - #[cfg(test)] - pub fn bus(&self) -> &T { - &self.bus - } - pub fn run_next(&mut self) -> CPURunState { self.check_and_run_dmc_transfer(); @@ -217,6 +212,14 @@ where CPURunState::Waiting } } + + pub fn bus(&self) -> &T { + &self.bus + } + + pub fn bus_mut(&mut self) -> &mut T { + &mut self.bus + } } // private diff --git a/plastic_core/src/display/tv.rs b/plastic_core/src/display/tv.rs index 85186e2..a496650 100644 --- a/plastic_core/src/display/tv.rs +++ b/plastic_core/src/display/tv.rs @@ -1,40 +1,27 @@ use super::color::Color; -use std::sync::{Arc, Mutex}; pub const TV_WIDTH: usize = 256; pub const TV_HEIGHT: usize = 240; -const COLOR_BYTES_LEN: usize = 4; +const COLOR_BYTES_LEN: usize = 3; pub const TV_BUFFER_SIZE: usize = TV_WIDTH * TV_HEIGHT * COLOR_BYTES_LEN; pub struct TV { - /// this buffer is being read by the UI provider, and written to by the PPU, - /// but for performance, we only update it once per frame, and the current - /// being drawn is being updated in [`building_pixels`] - pixels_to_display: Arc>>, + /// Current pixel buffer ready for display. + pixels_to_display: Box<[u8; TV_BUFFER_SIZE]>, /// A temporary buffer to holds the screen state while the PPU is drawing /// in the current frame - building_pixels: [Color; TV_WIDTH * TV_HEIGHT], - - /// A function to convert from [`Color`] to 4 byte value, which is used by - /// the UI provider - pixels_handler: fn(&Color) -> [u8; 4], + building_pixels: Box<[Color; TV_WIDTH * TV_HEIGHT]>, } impl TV { - pub fn new(pixels_handler: fn(&Color) -> [u8; COLOR_BYTES_LEN]) -> Self { + pub fn new() -> Self { Self { - pixels_to_display: Arc::new(Mutex::new(vec![0; TV_BUFFER_SIZE])), - building_pixels: [color!(0, 0, 0); TV_WIDTH * TV_HEIGHT], - pixels_handler, + pixels_to_display: Box::new([0; TV_BUFFER_SIZE]), + building_pixels: Box::new([color!(0, 0, 0); TV_WIDTH * TV_HEIGHT]), } } - /// this will be transfered to another thread - pub fn get_image_clone(&self) -> Arc>> { - self.pixels_to_display.clone() - } - /// update the pixel of the temporary buffer [`building_pixels`] pub fn set_pixel(&mut self, x: u32, y: u32, color: &Color) { let index = y as usize * TV_WIDTH + x as usize; @@ -45,26 +32,27 @@ impl TV { /// to tell the screen to copy and translate the [`Color`] data into the /// [`Arc`] shared screen buffer pub fn signal_end_of_frame(&mut self) { - if let Ok(mut buffer) = self.pixels_to_display.lock() { - for (result, color) in buffer - .chunks_exact_mut(COLOR_BYTES_LEN) - .zip(self.building_pixels.iter()) - { - result[0..4].copy_from_slice(&(self.pixels_handler)(color)); - } + for (result, color) in self + .pixels_to_display + .chunks_exact_mut(COLOR_BYTES_LEN) + .zip(self.building_pixels.iter()) + { + result[0..COLOR_BYTES_LEN].copy_from_slice(&[color.r, color.g, color.b]); } } /// resets and zero all buffers pub fn reset(&mut self) { - if let Ok(mut buffer) = self.pixels_to_display.lock() { - for i in buffer.iter_mut() { - *i = 0; - } + for i in self.pixels_to_display.iter_mut() { + *i = 0; } - for i in &mut self.building_pixels { + for i in self.building_pixels.as_mut() { *i = color!(0, 0, 0); } } + + pub fn display_pixel_buffer(&self) -> &[u8] { + self.pixels_to_display.as_ref() + } } diff --git a/plastic_core/src/frame_limiter.rs b/plastic_core/src/frame_limiter.rs deleted file mode 100644 index 68f3074..0000000 --- a/plastic_core/src/frame_limiter.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::thread; -use std::time; - -fn get_time() -> u64 { - time::SystemTime::now() - .duration_since(time::UNIX_EPOCH) - .unwrap_or_else(|e| e.duration()) - .as_micros() as u64 -} - -fn delay_1_ms() { - thread::sleep(time::Duration::from_millis(1)); -} - -fn add_percent(value: u64, percent: u64) -> u64 { - (value * 100 + value * percent) / 100 -} - -pub struct FrameLimiter { - target_fps: u64, - time_per_frame: u64, - begin_timestamp: u64, - last_frame_timestamp: u64, - average_frametime: u64, - average_delaytime: u64, - - frame_counter: u64, - frame_counter_timestamp: u64, - fps: u64, - - tolerance_percentage: u64, -} - -impl FrameLimiter { - pub fn new(target_fps: u64) -> FrameLimiter { - let time_per_frame = 1000000 / target_fps; - - FrameLimiter { - target_fps, - time_per_frame, - last_frame_timestamp: 0, - average_frametime: time_per_frame / 2, - average_delaytime: 1000, - frame_counter: 0, - frame_counter_timestamp: get_time(), - fps: 0, - begin_timestamp: 0, - tolerance_percentage: 5, - } - } - - fn time_left_until_deadline(&self, current: u64) -> u64 { - let target = self.last_frame_timestamp + self.time_per_frame; - if current > target { - 0 - } else { - target - current - } - } - - #[allow(dead_code)] - pub fn target_fps(&self) -> u64 { - self.target_fps - } - - #[allow(dead_code)] - pub fn fps(&self) -> u64 { - self.fps - } - - pub fn begin(&mut self) -> bool { - self.begin_timestamp = get_time(); - let time_left = self.time_left_until_deadline(self.begin_timestamp); - if time_left > add_percent(self.average_frametime, self.tolerance_percentage) { - delay_1_ms(); - - let elapsed = get_time() - self.begin_timestamp; - self.average_delaytime = (self.average_delaytime + elapsed) / 2; - - if self.average_frametime < self.average_delaytime { - self.average_frametime = self.average_delaytime; - } - - false - } else { - true - } - } - - pub fn end(&mut self) -> Option { - let current = get_time(); - let elapsed = current - self.begin_timestamp; - let elapsed_since_last_fps_update = current - self.frame_counter_timestamp; - - self.average_frametime = if elapsed < self.average_frametime { - (self.average_frametime * 2 + elapsed) / 3 - } else { - (self.average_frametime + elapsed * 2) / 3 - }; - - if self.average_frametime < 1000 { - self.average_frametime = 1000; - } - - if elapsed_since_last_fps_update > add_percent(self.time_per_frame, 20) { - self.last_frame_timestamp = current; - } else { - self.last_frame_timestamp += self.time_per_frame; - } - - self.frame_counter += 1; - if elapsed_since_last_fps_update >= 1000 * 1000 { - self.fps = (self.frame_counter * 1000 * 1000) / elapsed_since_last_fps_update; - self.frame_counter_timestamp = current; - self.frame_counter = 0; - - if self.fps.saturating_sub(self.target_fps) > 5 && self.tolerance_percentage > 2 { - self.tolerance_percentage -= 1; - } - - Some(self.fps) - } else { - None - } - } -} diff --git a/plastic_core/src/lib.rs b/plastic_core/src/lib.rs index cbcb888..51609f4 100644 --- a/plastic_core/src/lib.rs +++ b/plastic_core/src/lib.rs @@ -5,12 +5,12 @@ mod cartridge; mod controller; mod cpu6502; mod display; +pub mod misc; mod ppu2c02; #[cfg(test)] mod tests; -mod frame_limiter; pub mod nes; pub mod nes_controller { @@ -19,51 +19,6 @@ pub mod nes_controller { pub mod nes_display { pub use super::display::{Color, TV_BUFFER_SIZE, TV_HEIGHT, TV_WIDTH}; } - -use std::sync::{ - mpsc::{Receiver, Sender}, - Arc, Mutex, -}; - -pub enum UiEvent { - Exit, - Reset, - Pause, - Resume, - SaveState(u8), - LoadState(u8), - LoadRom(String), -} - -pub enum BackendEvent { - PresentStates(Vec), -} - -pub trait UiProvider { - // TODO: for now only supported are 32-bit size pixel data, maybe later we can - // support more - /// get the color converter for this UI provider, the reason this is good to have - /// is performance, as some UI for example use pixel data in form (RGBA) or (ARGB) - /// so this function will be called on every pixel set by the PPU in the time - /// it is set instead of doing it in the UI thread for the whole frame - /// - fn get_tv_color_converter() -> fn(&display::Color) -> [u8; 4]; - - /// initialize and run the UI loop, - /// this method will be called in another thread, so make sure it does not - /// return unless the UI is closed, if this function returns, the emulation - /// will stop and the emulator process will return - /// - /// [`ui_to_nes_sender`] a way for the UI to send messages to the backend nes - /// [`nes_to_ui_receiver`] a way for the Backend emulator to send messages to the ui - /// [`image`] contains the raw image data - /// [`ctrl_state`] is the controller state, the provider should change this - /// based on buttons presses and releases - fn run_ui_loop( - &mut self, - ui_to_nes_sender: Sender, - nes_to_ui_receiver: Receiver, - image: Arc>>, - ctrl_state: Arc>, - ); +pub mod nes_audio { + pub use super::apu2a03::SAMPLE_RATE; } diff --git a/plastic_core/src/misc.rs b/plastic_core/src/misc.rs new file mode 100644 index 0000000..fd649ce --- /dev/null +++ b/plastic_core/src/misc.rs @@ -0,0 +1,110 @@ +//! Some common tools used for the emulator UIs to limit FPs + +use std::time::{Duration, Instant}; + +pub struct MovingAverage { + values: [f64; 100], + current_index: usize, + sum: f64, +} + +impl MovingAverage { + pub fn new() -> Self { + Self { + values: [0.0; 100], + current_index: 0, + sum: 0.0, + } + } + + pub fn add(&mut self, value: f64) { + self.sum -= self.values[self.current_index]; + self.sum += value; + self.values[self.current_index] = value; + self.current_index = (self.current_index + 1) % self.values.len(); + } + + pub fn average(&self) -> f64 { + self.sum / self.values.len() as f64 + } +} + +/// Moving average fps counter +pub struct Fps { + moving_average: MovingAverage, + last_frame: Instant, + pub target_fps: f64, +} + +impl Fps { + pub fn new(target_fps: f64) -> Self { + Self { + moving_average: MovingAverage::new(), + last_frame: Instant::now(), + target_fps, + } + } + + // check if we should start a new frame + // return true if we should start a new frame + // return false if we should skip this frame + pub fn start_frame(&mut self) -> bool { + let duration_per_frame = Duration::from_secs_f64(1.0 / self.target_fps); + let elapsed = self.last_frame.elapsed(); + if elapsed < duration_per_frame { + return false; + } + + let now = Instant::now(); + let delta = now.duration_since(self.last_frame).as_secs_f64(); + self.last_frame = now; + + self.moving_average.add(delta); + true + } + + pub fn fps(&self) -> f64 { + 1.0 / self.moving_average.average() + } + + pub fn remaining(&self) -> Option { + let duration_per_frame = Duration::from_secs_f64(1.0 / self.target_fps); + + let elapsed = self.last_frame.elapsed(); + + if elapsed >= duration_per_frame { + return None; + } + let remaining = duration_per_frame - elapsed; + Some(remaining) + } +} + +/// Process the audio buffer to make it stereo +/// Also add or remove samples to match the current speed +/// more speed_modifier means faster speed and less samples +/// `speed_modifier == 1.0` means normal speed +pub fn process_audio(audio_buffer: &[f32], speed_modifier: f32) -> Vec { + let target_len = (audio_buffer.len() as f32 * speed_modifier).ceil() as usize; + let mut adjusted_buffer = Vec::with_capacity(target_len * 2); + + for i in 0..target_len { + let src_index_f = i as f32 / speed_modifier; + let src_index = src_index_f.floor() as usize; + let next_index = std::cmp::min(src_index + 1, audio_buffer.len() - 1); + let fraction = src_index_f.fract(); + + let sample = if src_index < audio_buffer.len() { + let current_sample = audio_buffer[src_index]; + let next_sample = audio_buffer[next_index]; + current_sample * (1.0 - fraction) + next_sample * fraction + } else { + *audio_buffer.last().unwrap_or(&0.0) + }; + // Add the sample twice for left and right channels + adjusted_buffer.push(sample); + adjusted_buffer.push(sample); + } + + adjusted_buffer +} diff --git a/plastic_core/src/nes.rs b/plastic_core/src/nes.rs index 88c4b30..143a802 100644 --- a/plastic_core/src/nes.rs +++ b/plastic_core/src/nes.rs @@ -5,21 +5,15 @@ use crate::common::{ save_state::{Savable, SaveError}, Bus, Device, MirroringProvider, }; -use crate::controller::{Controller, StandardNESControllerState}; -use crate::cpu6502::{CPUBusTrait, CPU6502}; +use crate::controller::Controller; +use crate::cpu6502::{CPUBusTrait, CPURunState, CPU6502}; use crate::display::TV; use crate::ppu2c02::{Palette, VRam, PPU2C02}; -use directories_next::ProjectDirs; -use regex::{self, Regex}; use std::cell::Cell; use std::cell::RefCell; -use std::fs::{self, File}; use std::io::Read; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::rc::Rc; -use std::sync::{mpsc::channel, Arc, Mutex}; - -use super::{frame_limiter::FrameLimiter, BackendEvent, UiEvent, UiProvider}; struct PPUBus { cartridge: Rc>, @@ -80,8 +74,8 @@ impl Savable for PPUBus { struct CPUBus { ram: [u8; 0x800], cartridge: Rc>, - ppu: Rc>>, - apu: Rc>, + ppu: PPU2C02, + apu: APU2A03, contoller: Controller, irq_pin_change_requested: Cell, } @@ -89,8 +83,8 @@ struct CPUBus { impl CPUBus { pub fn new( cartridge: Rc>, - ppu: Rc>>, - apu: Rc>, + ppu: PPU2C02, + apu: APU2A03, contoller: Controller, ) -> Self { CPUBus { @@ -102,21 +96,22 @@ impl CPUBus { irq_pin_change_requested: Cell::new(false), } } + + fn contoller_mut(&mut self) -> &mut Controller { + &mut self.contoller + } } impl CPUBusTrait for CPUBus { fn read(&self, address: u16) -> u8 { match address { 0x0000..=0x1FFF => self.ram[(address & 0x7FF) as usize], - 0x2000..=0x3FFF => self - .ppu - .borrow() - .read(0x2000 | (address & 0x7), Device::Cpu), - 0x4000..=0x4013 => self.apu.borrow().read(address, Device::Cpu), - 0x4014 => self.ppu.borrow().read(address, Device::Cpu), - 0x4015 => self.apu.borrow().read(address, Device::Cpu), + 0x2000..=0x3FFF => self.ppu.read(0x2000 | (address & 0x7), Device::Cpu), + 0x4000..=0x4013 => self.apu.read(address, Device::Cpu), + 0x4014 => self.ppu.read(address, Device::Cpu), + 0x4015 => self.apu.read(address, Device::Cpu), 0x4016 => self.contoller.read(address, Device::Cpu), - 0x4017 => self.apu.borrow().read(address, Device::Cpu), + 0x4017 => self.apu.read(address, Device::Cpu), 0x4018..=0x401F => { // unused CPU test mode registers 0 @@ -128,16 +123,12 @@ impl CPUBusTrait for CPUBus { fn write(&mut self, address: u16, data: u8) { match address { 0x0000..=0x1FFF => self.ram[(address & 0x7FF) as usize] = data, - 0x2000..=0x3FFF => { - self.ppu - .borrow_mut() - .write(0x2000 | (address & 0x7), data, Device::Cpu) - } - 0x4000..=0x4013 => self.apu.borrow_mut().write(address, data, Device::Cpu), - 0x4014 => self.ppu.borrow_mut().write(address, data, Device::Cpu), - 0x4015 => self.apu.borrow_mut().write(address, data, Device::Cpu), + 0x2000..=0x3FFF => self.ppu.write(0x2000 | (address & 0x7), data, Device::Cpu), + 0x4000..=0x4013 => self.apu.write(address, data, Device::Cpu), + 0x4014 => self.ppu.write(address, data, Device::Cpu), + 0x4015 => self.apu.write(address, data, Device::Cpu), 0x4016 => self.contoller.write(address, data, Device::Cpu), - 0x4017 => self.apu.borrow_mut().write(address, data, Device::Cpu), + 0x4017 => self.apu.write(address, data, Device::Cpu), 0x4018..=0x401F => { // unused CPU test mode registers } @@ -169,51 +160,51 @@ impl Savable for CPUBus { impl PPUCPUConnection for CPUBus { fn is_nmi_pin_set(&self) -> bool { - self.ppu.borrow().is_nmi_pin_set() + self.ppu.is_nmi_pin_set() } fn clear_nmi_pin(&mut self) { - self.ppu.borrow_mut().clear_nmi_pin() + self.ppu.clear_nmi_pin() } fn is_dma_request(&self) -> bool { - self.ppu.borrow_mut().is_dma_request() + self.ppu.is_dma_request() } fn clear_dma_request(&mut self) { - self.ppu.borrow_mut().clear_dma_request() + self.ppu.clear_dma_request() } fn dma_address(&mut self) -> u8 { - self.ppu.borrow_mut().dma_address() + self.ppu.dma_address() } fn send_oam_data(&mut self, address: u8, data: u8) { - self.ppu.borrow_mut().send_oam_data(address, data) + self.ppu.send_oam_data(address, data) } } impl APUCPUConnection for CPUBus { fn request_dmc_reader_read(&self) -> Option { - self.apu.borrow().request_dmc_reader_read() + self.apu.request_dmc_reader_read() } fn submit_dmc_buffer_byte(&mut self, byte: u8) { - self.apu.borrow_mut().submit_dmc_buffer_byte(byte) + self.apu.submit_dmc_buffer_byte(byte) } } impl CPUIrqProvider for CPUBus { fn is_irq_change_requested(&self) -> bool { - let result = self.apu.borrow().is_irq_change_requested() - || self.cartridge.borrow().is_irq_change_requested(); + let result = + self.apu.is_irq_change_requested() || self.cartridge.borrow().is_irq_change_requested(); self.irq_pin_change_requested.set(result); result } fn irq_pin_state(&self) -> bool { if self.irq_pin_change_requested.get() { - let mut result = self.apu.borrow().irq_pin_state(); + let mut result = self.apu.irq_pin_state(); if self.cartridge.borrow().is_irq_change_requested() { result = result || self.cartridge.borrow().irq_pin_state(); } @@ -226,69 +217,47 @@ impl CPUIrqProvider for CPUBus { fn clear_irq_request_pin(&mut self) { *self.irq_pin_change_requested.get_mut() = false; self.cartridge.borrow_mut().clear_irq_request_pin(); - self.apu.borrow_mut().clear_irq_request_pin(); + self.apu.clear_irq_request_pin(); } } -pub struct NES { +pub struct NES { cartridge: Rc>, cpu: CPU6502, - ppu: Rc>>, - apu: Rc>, - image: Arc>>, - ctrl_state: Arc>, - - ui: Option

, // just to hold the UI object (it will be taken in the main loop) - - paused: bool, } -impl NES

{ - pub fn new(filename: &str, ui: P) -> Result { +impl NES { + pub fn new>(filename: P) -> Result { let cartridge = Cartridge::from_file(filename)?; - Ok(Self::create_nes(cartridge, ui)) + Ok(Self::create_nes(cartridge)) } - pub fn new_without_file(ui: P) -> Self { + pub fn new_without_file() -> Self { let cartridge = Cartridge::new_without_file(); - Self::create_nes(cartridge, ui) + Self::create_nes(cartridge) } - fn create_nes(cartridge: Cartridge, ui: P) -> Self { + fn create_nes(cartridge: Cartridge) -> Self { let cartridge = Rc::new(RefCell::new(cartridge)); let ppubus = PPUBus::new(cartridge.clone()); - let tv = TV::new(P::get_tv_color_converter()); - let image = tv.get_image_clone(); + let tv = TV::new(); let ppu = PPU2C02::new(ppubus, tv); - let ppu = Rc::new(RefCell::new(ppu)); - - let apu = Rc::new(RefCell::new(APU2A03::new())); + let apu = APU2A03::new(); let ctrl = Controller::new(); - let ctrl_state = ctrl.get_primary_controller_state(); - let cpubus = CPUBus::new(cartridge.clone(), ppu.clone(), apu.clone(), ctrl); + let cpubus = CPUBus::new(cartridge.clone(), ppu, apu, ctrl); - let cpu = CPU6502::new(cpubus); + let mut cpu = CPU6502::new(cpubus); - let paused = cartridge.borrow().is_empty(); + cpu.reset(); - Self { - cartridge, - cpu, - ppu, - apu, - image, - ctrl_state, - ui: Some(ui), - - paused, - } + Self { cartridge, cpu } } pub fn reset(&mut self) { @@ -297,263 +266,113 @@ impl NES

{ let ppubus = PPUBus::new(self.cartridge.clone()); - self.ppu.borrow_mut().reset(ppubus); - - self.apu.replace(APU2A03::new()); + self.cpu.bus_mut().ppu.reset(ppubus); - self.paused = self.cartridge.borrow().is_empty(); + self.cpu.bus_mut().apu = APU2A03::new(); } - fn get_base_save_state_folder(&self) -> Option { - if let Some(proj_dirs) = ProjectDirs::from("Amjad50", "Plastic", "Plastic") { - let base_saved_states_dir = proj_dirs.data_local_dir().join("saved_states"); - // Linux: /home/../.local/share/plastic/saved_states - // Windows: C:\Users\..\AppData\Local\Plastic\Plastic\data\saved_states - // macOS: /Users/../Library/Application Support/Amjad50.Plastic.Plastic/saved_states + pub fn clock_for_frame(&mut self) { + if self.cartridge.borrow().is_empty() { + return; + } + + const N: usize = 29780; // number of CPU cycles per loop, one full frame - fs::create_dir_all(&base_saved_states_dir).ok()?; + for _ in 0..N { + self.cpu.bus_mut().apu.clock(); - Some(base_saved_states_dir) - } else { - None + self.cpu.run_next(); + { + let ppu = &mut self.cpu.bus_mut().ppu; + ppu.clock(); + ppu.clock(); + ppu.clock(); + } } } - fn get_save_state_file_path(&self, slot: u8) -> Option> { + pub fn clock(&mut self) -> Option { if self.cartridge.borrow().is_empty() { return None; } - let cartridge_path = self.cartridge.borrow().cartridge_path().to_path_buf(); - - self.get_base_save_state_folder() - .map(|base_saved_states_dir| { - base_saved_states_dir - .join(format!( - "{}_{}.pst", - cartridge_path.file_stem().unwrap().to_string_lossy(), - slot - )) - .into_boxed_path() - }) - } + self.cpu.bus_mut().apu.clock(); - fn get_present_save_states(&self) -> Option> { - if self.cartridge.borrow().is_empty() { - return None; + let r = self.cpu.run_next(); + { + let ppu = &mut self.cpu.bus_mut().ppu; + ppu.clock(); + ppu.clock(); + ppu.clock(); } - let cartridge_path = self.cartridge.borrow().cartridge_path().to_path_buf(); - - if let Some(base_saved_states_dir) = self.get_base_save_state_folder() { - let saved_states_files_regex = Regex::new(&format!( - r"{}_(\d*).pst", - regex::escape(&cartridge_path.file_stem().unwrap().to_string_lossy()), - )) - .ok()?; - - Some( - fs::read_dir(base_saved_states_dir) - .ok()? - .filter_map(|path| { - if path.as_ref().ok()?.file_type().ok()?.is_file() { - Some(path.ok()?.path()) - } else { - None - } - }) - .filter_map(|path| { - saved_states_files_regex - .captures(path.file_name()?.to_str()?)? - .get(1)? - .as_str() - .parse::() - .ok() - }) - .collect::>(), - ) - } else { - None - } + Some(r) } - pub fn save_state(&self, slot: u8) -> Result<(), SaveError> { - if let Some(path) = self.get_save_state_file_path(slot) { - let mut file = File::create(path)?; - - self.cartridge.borrow().save(&mut file)?; - self.cpu.save(&mut file)?; - self.ppu.borrow().save(&mut file)?; - self.apu.borrow().save(&mut file)?; - - Ok(()) - } else { - Err(SaveError::Others) - } + /// Return the pixel buffer as RGB format + pub fn pixel_buffer(&self) -> &[u8] { + self.cpu.bus().ppu.tv().display_pixel_buffer() } - pub fn load_state(&mut self, slot: u8) -> Result<(), SaveError> { - if let Some(path) = self.get_save_state_file_path(slot) { - if path.exists() { - let mut file = File::open(path)?; - - self.cartridge.borrow_mut().load(&mut file)?; - self.cpu.load(&mut file)?; - self.ppu.borrow_mut().load(&mut file)?; - self.apu.borrow_mut().load(&mut file)?; - - let mut rest = Vec::new(); - file.read_to_end(&mut rest)?; - - if !rest.is_empty() { - return Err(SaveError::Others); - } + pub fn audio_buffer(&mut self) -> Vec { + self.cpu.bus().apu.take_audio_buffer() + } - if !self.paused { - self.apu.borrow().play(); - } + pub fn is_empty(&self) -> bool { + self.cartridge.borrow().is_empty() + } - Ok(()) - } else { - Err(SaveError::IoError(std::io::Error::new( - std::io::ErrorKind::NotFound, - "save file not found", - ))) - } - } else { - Err(SaveError::Others) - } + pub fn controller(&mut self) -> &mut Controller { + self.cpu.bus_mut().contoller_mut() } - /// calculate a new view based on the window size - pub fn run(&mut self) { - let image = self.image.clone(); - let ctrl_state = self.ctrl_state.clone(); - let mut frame_limiter = FrameLimiter::new(60); + pub fn save_state_file_name(&self, slot: u8) -> Option { + if self.cartridge.borrow().is_empty() { + return None; + } - let (ui_to_nes_sender, ui_to_nes_receiver) = channel::(); - let (nes_to_ui_sender, nes_to_ui_receiver) = channel::(); + let cart = self.cartridge.borrow(); + let cartridge_path = cart.cartridge_path(); - let mut ui = self.ui.take().unwrap(); + Some(format!( + "{}_{}.pst", + cartridge_path.file_stem().unwrap().to_string_lossy(), + slot + )) + } - let ui_thread_handler = std::thread::spawn(move || { - ui.run_ui_loop( - ui_to_nes_sender.clone(), - nes_to_ui_receiver, - image, - ctrl_state, - ); - ui_to_nes_sender.send(UiEvent::Exit).unwrap(); - }); + pub fn save_state(&self, mut writer: W) -> Result<(), SaveError> { + self.cartridge.borrow().save(&mut writer)?; + self.cpu.save(&mut writer)?; + self.cpu.bus().ppu.save(&mut writer)?; + self.cpu.bus().apu.save(&mut writer)?; - self.cpu.reset(); + Ok(()) + } - const N: usize = 29780; // number of CPU cycles per loop, one full frame + pub fn load_state(&mut self, mut reader: R) -> Result<(), SaveError> { + self.cartridge.borrow_mut().load(&mut reader)?; + self.cpu.load(&mut reader)?; + self.cpu.bus_mut().ppu.load(&mut reader)?; + self.cpu.bus_mut().apu.load(&mut reader)?; - // just a way to duplicate code, its not meant to be efficient way to do it - // I used this, since `self` cannot be referenced here and anywhere else at - // the same time. - macro_rules! handle_apu_after_reset { - () => { - if !self.paused { - self.apu.borrow().play(); - } - }; - } + let mut rest = Vec::new(); + reader.read_to_end(&mut rest)?; - macro_rules! send_present_save_states_to_ui { - () => { - if let Some(states) = self.get_present_save_states() { - nes_to_ui_sender - .send(BackendEvent::PresentStates(states)) - .unwrap(); - } - }; + if !rest.is_empty() { + return Err(SaveError::ContainExtraData); } - // first time - handle_apu_after_reset!(); - - send_present_save_states_to_ui!(); - - // run the emulator loop - loop { - // check for events - if let Ok(event) = ui_to_nes_receiver.try_recv() { - match event { - UiEvent::Exit => break, - UiEvent::Reset => { - self.reset(); - handle_apu_after_reset!(); - send_present_save_states_to_ui!(); - } - - UiEvent::LoadRom(file_location) => { - let cartridge = Cartridge::from_file(file_location); - if let Ok(cartridge) = cartridge { - self.cartridge.replace(cartridge); - self.reset(); - handle_apu_after_reset!(); - } else { - println!("This game is not supported yet"); - } - send_present_save_states_to_ui!(); - } - UiEvent::Pause => { - self.paused = true; - self.apu.borrow_mut().pause(); - } - UiEvent::Resume => { - // only resume if we can - if !self.cartridge.borrow().is_empty() { - self.paused = false; - self.apu.borrow_mut().play(); - self.apu.borrow_mut().empty_queue(); - } - } - UiEvent::SaveState(slot) => { - // only if there is a game - if !self.cartridge.borrow().is_empty() { - if let Err(err) = self.save_state(slot) { - eprintln!("Error in saving the state: {}", err); - } - send_present_save_states_to_ui!(); - } - } - UiEvent::LoadState(slot) => { - // only if there is a game - if !self.cartridge.borrow().is_empty() { - if let Err(err) = self.load_state(slot) { - eprintln!("Error in loading the state: {}", err); - } - send_present_save_states_to_ui!(); - } - } - } - } - - if self.paused { - std::thread::sleep(std::time::Duration::from_millis(50)); - continue; - } - - if frame_limiter.begin() { - for _ in 0..N { - self.apu.borrow_mut().clock(); - - self.cpu.run_next(); - { - let mut ppu = self.ppu.borrow_mut(); - ppu.clock(); - ppu.clock(); - ppu.clock(); - } - } + Ok(()) + } - frame_limiter.end(); - } - } + #[cfg(test)] + pub(crate) fn cpu_bus(&self) -> &impl CPUBusTrait { + self.cpu.bus() + } - ui_thread_handler.join().unwrap(); + #[cfg(test)] + pub(crate) fn ppu_bus(&self) -> &impl Bus { + self.cpu.bus().ppu.ppu_bus() } } diff --git a/plastic_core/src/ppu2c02/mod.rs b/plastic_core/src/ppu2c02/mod.rs index 06b2cbc..c09b072 100644 --- a/plastic_core/src/ppu2c02/mod.rs +++ b/plastic_core/src/ppu2c02/mod.rs @@ -1139,6 +1139,10 @@ where self.dma_request_address = state.dma_request_address; self.is_odd_frame = state.is_odd_frame; } + + pub fn tv(&self) -> &TV { + &self.tv + } } impl PPUCPUConnection for PPU2C02 diff --git a/plastic_core/src/tests/blargg_tests.rs b/plastic_core/src/tests/blargg_tests.rs index 93b9859..420221e 100644 --- a/plastic_core/src/tests/blargg_tests.rs +++ b/plastic_core/src/tests/blargg_tests.rs @@ -6,7 +6,6 @@ fn run_sprite_hit_test(filename: &str) -> Result<(), TestError> { let result_memory_address = 0x00F8; let mut nes = NesTester::new(filename)?; - nes.reset_cpu(); // this is the top-left pixel of the word "PASSED" or "FAILED" nes.clock_until_pixel_appears(17, 48, 0x30); @@ -22,7 +21,6 @@ fn run_sprite_hit_test(filename: &str) -> Result<(), TestError> { fn run_blargg_test_00f0(filename: &str) -> Result<(), TestError> { let mut nes = NesTester::new(filename)?; - nes.reset_cpu(); nes.clock_until_infinite_loop(); @@ -39,7 +37,6 @@ fn run_blargg_test_6000_80(filename: &str) -> Result<(), TestError> { let result_memory_address = 0x6000; let mut nes = NesTester::new(filename)?; - nes.reset_cpu(); // first loop until an infnite loop (this infinite loop might be the // end or not), then loop until the value of `0x6000` is not `0x80` @@ -96,7 +93,6 @@ mod ppu { let result_memory_address = 0x00f0; let mut nes = NesTester::new(filename)?; - nes.reset_cpu(); // 2 NMIs should occure nes.clock_until_nmi(); diff --git a/plastic_core/src/tests/mod.rs b/plastic_core/src/tests/mod.rs index 9730a6e..0751202 100644 --- a/plastic_core/src/tests/mod.rs +++ b/plastic_core/src/tests/mod.rs @@ -1,28 +1,16 @@ -use crate::apu2a03::APU2A03; -use crate::cartridge::{Cartridge, CartridgeError}; -use crate::common::{ - interconnection::*, - save_state::{Savable, SaveError}, - Bus, Device, -}; -use crate::cpu6502::{CPUBusTrait, CPURunState, CPU6502}; -use crate::display::{COLORS, TV}; -use crate::ppu2c02::{Palette, VRam, PPU2C02}; +use crate::cartridge::CartridgeError; +use crate::common::{Bus, Device}; +use crate::cpu6502::{CPUBusTrait, CPURunState}; +use crate::display::{COLORS, TV_WIDTH}; +use crate::nes::NES; use std::{ - cell::{Cell, RefCell}, convert::From, error::Error, fmt::{Debug, Display, Formatter, Result as fmtResult}, - rc::Rc, - sync::{Arc, Mutex}, }; mod blargg_tests; - -// FIXME: used constants hosted in TV -const TV_WIDTH: u32 = 256; -#[allow(dead_code)] -const TV_HEIGHT: u32 = 240; +mod save_state; pub enum TestError { CartridgeError(CartridgeError), @@ -58,262 +46,31 @@ impl From for TestError { } } -struct PPUBus { - cartridge: Rc>, - vram: VRam, - palettes: Palette, -} - -impl PPUBus { - pub fn new(cartridge: Rc>) -> Self { - PPUBus { - cartridge: cartridge.clone(), - vram: VRam::new(cartridge), - palettes: Palette::new(), - } - } -} - -impl Bus for PPUBus { - fn read(&self, address: u16, device: Device) -> u8 { - match address { - 0x0000..=0x1FFF => self.cartridge.borrow().read(address, device), - 0x2000..=0x3EFF => self.vram.read(address & 0x2FFF, device), - 0x3F00..=0x3FFF => self.palettes.read(address, device), - // mirror - 0x4000..=0xFFFF => self.read(address & 0x3FFF, device), - } - } - fn write(&mut self, address: u16, data: u8, device: Device) { - match address { - 0x0000..=0x1FFF => self.cartridge.borrow_mut().write(address, data, device), - 0x2000..=0x3EFF => self.vram.write(address & 0x2FFF, data, device), - 0x3F00..=0x3FFF => self.palettes.write(address, data, device), - // mirror - 0x4000..=0xFFFF => self.write(address & 0x3FFF, data, device), - } - } -} - -impl Savable for PPUBus { - fn save(&self, _writer: &mut W) -> Result<(), SaveError> { - unreachable!() - } - - fn load(&mut self, _reader: &mut R) -> Result<(), SaveError> { - unreachable!() - } -} - -struct CPUBus { - cartridge: Rc>, - ram: [u8; 0x800], - ppu: Rc>>, - apu: Rc>, - irq_pin_change_requested: Cell, -} - -impl CPUBus { - pub fn new( - cartridge: Rc>, - ppu: Rc>>, - apu: Rc>, - ) -> Self { - CPUBus { - cartridge, - ram: [0; 0x800], - ppu, - apu, - irq_pin_change_requested: Cell::new(false), - } - } -} - -impl CPUBusTrait for CPUBus { - fn read(&self, address: u16) -> u8 { - match address { - 0x0000..=0x1FFF => self.ram[(address & 0x7FF) as usize], - 0x2000..=0x3FFF => self - .ppu - .borrow() - .read(0x2000 | (address & 0x7), Device::Cpu), - 0x4000..=0x4013 => self.apu.borrow().read(address, Device::Cpu), - 0x4014 => self.ppu.borrow().read(address, Device::Cpu), - 0x4015 => self.apu.borrow().read(address, Device::Cpu), - 0x4016 => { - // controller - 0 - } - 0x4017 => self.apu.borrow().read(address, Device::Cpu), - 0x4018..=0x401F => { - // unused CPU test mode registers - 0 - } - 0x4020..=0xFFFF => self.cartridge.borrow().read(address, Device::Cpu), - } - } - fn write(&mut self, address: u16, data: u8) { - match address { - 0x0000..=0x1FFF => self.ram[(address & 0x7FF) as usize] = data, - 0x2000..=0x3FFF => { - self.ppu - .borrow_mut() - .write(0x2000 | (address & 0x7), data, Device::Cpu) - } - 0x4000..=0x4013 => self.apu.borrow_mut().write(address, data, Device::Cpu), - 0x4014 => self.ppu.borrow_mut().write(address, data, Device::Cpu), - 0x4015 => self.apu.borrow_mut().write(address, data, Device::Cpu), - 0x4016 => { - // controller - } - 0x4017 => self.apu.borrow_mut().write(address, data, Device::Cpu), - 0x4018..=0x401F => { - // unused CPU test mode registers - } - 0x4020..=0xFFFF => self - .cartridge - .borrow_mut() - .write(address, data, Device::Cpu), - }; - } - - fn reset(&mut self) { - self.ram = [0; 0x800]; - } -} - -impl Savable for CPUBus { - fn save(&self, _: &mut W) -> Result<(), SaveError> { - unreachable!() - } - - fn load(&mut self, _: &mut R) -> Result<(), SaveError> { - unreachable!() - } -} - -impl PPUCPUConnection for CPUBus { - fn is_nmi_pin_set(&self) -> bool { - self.ppu.borrow().is_nmi_pin_set() - } - - fn clear_nmi_pin(&mut self) { - self.ppu.borrow_mut().clear_nmi_pin() - } - - fn is_dma_request(&self) -> bool { - self.ppu.borrow_mut().is_dma_request() - } - - fn clear_dma_request(&mut self) { - self.ppu.borrow_mut().clear_dma_request() - } - - fn dma_address(&mut self) -> u8 { - self.ppu.borrow_mut().dma_address() - } - - fn send_oam_data(&mut self, address: u8, data: u8) { - self.ppu.borrow_mut().send_oam_data(address, data) - } -} - -impl APUCPUConnection for CPUBus { - fn request_dmc_reader_read(&self) -> Option { - self.apu.borrow().request_dmc_reader_read() - } - - fn submit_dmc_buffer_byte(&mut self, byte: u8) { - self.apu.borrow_mut().submit_dmc_buffer_byte(byte) - } -} - -impl CPUIrqProvider for CPUBus { - fn is_irq_change_requested(&self) -> bool { - let result = self.apu.borrow().is_irq_change_requested() - || self.cartridge.borrow().is_irq_change_requested(); - - self.irq_pin_change_requested.set(result); - result - } - - fn irq_pin_state(&self) -> bool { - if self.irq_pin_change_requested.get() { - let mut result = self.apu.borrow().irq_pin_state(); - if self.cartridge.borrow().is_irq_change_requested() { - result = result || self.cartridge.borrow().irq_pin_state(); - } - result - } else { - false - } - } - - fn clear_irq_request_pin(&mut self) { - *self.irq_pin_change_requested.get_mut() = false; - self.cartridge.borrow_mut().clear_irq_request_pin(); - self.apu.borrow_mut().clear_irq_request_pin(); - } -} - pub struct NesTester { - cpu: CPU6502, - ppu: Rc>>, - tv_image: Arc>>, - apu: Rc>, + nes: NES, } impl NesTester { pub fn new(filename: &str) -> Result { - let cartridge = Rc::new(RefCell::new(Cartridge::from_file(filename)?)); - - let ppubus = PPUBus::new(cartridge.clone()); - - let tv = TV::new(|color| [color.r, color.g, color.b, 0xFF]); - let tv_image = tv.get_image_clone(); - - let ppu = Rc::new(RefCell::new(PPU2C02::new(ppubus, tv))); - - let apu = Rc::new(RefCell::new(APU2A03::new())); - - let cpubus = CPUBus::new(cartridge, ppu.clone(), apu.clone()); - - let cpu = CPU6502::new(cpubus); - - Ok(Self { - cpu, - ppu, - tv_image, - apu, - }) - } + let nes = NES::new(filename)?; - pub fn reset_cpu(&mut self) { - self.cpu.reset(); + Ok(Self { nes }) } pub fn cpu_read_address(&self, address: u16) -> u8 { - self.cpu.bus().read(address) + self.nes.cpu_bus().read(address) } pub fn ppu_read_address(&self, address: u16) -> u8 { - self.ppu.borrow().ppu_bus().read(address, Device::Ppu) + self.nes.ppu_bus().read(address, Device::Ppu) } - pub fn clock(&mut self) -> CPURunState { - self.apu.borrow_mut().clock(); - - let return_value = self.cpu.run_next(); - - { - let mut ppu = self.ppu.borrow_mut(); - - ppu.clock(); - ppu.clock(); - ppu.clock(); - } + pub fn pixel_buffer(&self) -> &[u8] { + self.nes.pixel_buffer() + } - return_value + pub fn clock(&mut self) -> CPURunState { + self.nes.clock().unwrap() } pub fn clock_until_infinite_loop(&mut self) { @@ -337,7 +94,7 @@ impl NesTester { loop { self.clock(); - if self.cpu.bus().read(address) != data { + if self.cpu_read_address(address) != data { break; } } @@ -348,24 +105,25 @@ impl NesTester { /// /// this check is done manually now, not sure if it should be added /// to `display::TV` or not - #[allow(clippy::identity_op)] pub fn clock_until_pixel_appears(&mut self, x: u32, y: u32, color_code: u8) { loop { self.clock(); - let index = (y * TV_WIDTH + x) as usize * 4; + let index = (y * TV_WIDTH as u32 + x) as usize * 3; - if let Ok(image) = self.tv_image.lock() { - let color = &COLORS[color_code as usize]; + let pixel_buffer = self.pixel_buffer(); + let color = &COLORS[color_code as usize]; - if image[index + 0] == color.r - && image[index + 1] == color.g - && image[index + 2] == color.b - && image[index + 3] == 0xFF - { - break; - } + if pixel_buffer[index] == color.r + && pixel_buffer[index + 1] == color.g + && pixel_buffer[index + 2] == color.b + { + break; } } } + + pub fn clock_for_frame(&mut self) { + self.nes.clock_for_frame() + } } diff --git a/plastic_core/src/tests/save_state.rs b/plastic_core/src/tests/save_state.rs new file mode 100644 index 0000000..f177bb2 --- /dev/null +++ b/plastic_core/src/tests/save_state.rs @@ -0,0 +1,50 @@ +use std::io::Cursor; + +use crate::tests::NesTester; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum TestState { + Running, + Passed, + Failed, +} +const BLARGG_MEM_RESULT: u16 = 0x6000; +const BLARGG_STATE_RUNNING: u8 = 0x80; + +fn get_test_state(nes: &NesTester) -> TestState { + match nes.cpu_read_address(BLARGG_MEM_RESULT) { + BLARGG_STATE_RUNNING => TestState::Running, + 0 => TestState::Passed, + _ => TestState::Failed, + } +} + +#[test] +fn save_load_test() { + // 0- perform normal test (this part should always pass) + let file_path = "../test_roms/instr_test-v5/all_instrs.nes"; + + // 1- make sure after start and advancing 2 frames does not pass the test + let mut nes = NesTester::new(file_path).unwrap(); + nes.clock_for_frame(); + nes.clock_for_frame(); + assert_eq!(get_test_state(&nes), TestState::Running); + + // create it again, and then run until it passes + nes = NesTester::new(file_path).unwrap(); + nes.clock_until_infinite_loop(); + nes.clock_until_memory_neq(BLARGG_MEM_RESULT, BLARGG_STATE_RUNNING); + assert_eq!(get_test_state(&nes), TestState::Passed); + + // 2- save the state at which it was passing + let mut buffer = Vec::new(); + nes.nes.save_state(&mut buffer).unwrap(); + + // 3- create a new object and load the state + nes = NesTester::new(file_path).unwrap(); + let mut c = Cursor::new(&buffer); + nes.nes.load_state(&mut c).unwrap(); + assert!(c.position() == buffer.len() as u64); + + assert_eq!(get_test_state(&nes), TestState::Passed); +} diff --git a/plastic_ui/Cargo.toml b/plastic_ui/Cargo.toml new file mode 100644 index 0000000..ccbdb93 --- /dev/null +++ b/plastic_ui/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "plastic_ui" +version = "0.1.0" +edition = "2021" + +[dependencies] +plastic_core = { path = "../plastic_core" } +egui = "0.28" +egui-winit = "0.28" +directories = "5.0" +eframe = "0.28" +rfd = "0.14" +dynwave = "0.1" diff --git a/plastic_ui/src/main.rs b/plastic_ui/src/main.rs new file mode 100644 index 0000000..85ebe5e --- /dev/null +++ b/plastic_ui/src/main.rs @@ -0,0 +1,411 @@ +use std::{fs, path::PathBuf}; + +use directories::ProjectDirs; +use dynwave::AudioPlayer; +use plastic_core::{ + misc::{process_audio, Fps}, + nes::NES, + nes_audio::SAMPLE_RATE, + nes_controller::StandardNESKey, + nes_display::{TV_HEIGHT, TV_WIDTH}, +}; + +// 60 FPS gives audio glitches +const TARGET_FPS: f64 = 61.; + +const MIN_STATE_SLOT: u8 = 0; +const MAX_STATE_SLOT: u8 = 9; + +fn base_save_state_folder() -> Option { + if let Some(proj_dirs) = ProjectDirs::from("Amjad50", "Plastic", "Plastic") { + let base_saved_states_dir = proj_dirs.data_local_dir().join("saved_states"); + // Linux: /home/../.local/share/plastic/saved_states + // Windows: C:\Users\..\AppData\Local\Plastic\Plastic\data\saved_states + // macOS: /Users/../Library/Application Support/Amjad50.Plastic.Plastic/saved_states + + fs::create_dir_all(&base_saved_states_dir).ok()?; + + Some(base_saved_states_dir) + } else { + None + } +} + +const OPEN_SHORTCUT: egui::KeyboardShortcut = + egui::KeyboardShortcut::new(egui::Modifiers::CTRL, egui::Key::O); +const RESET_SHORTCUT: egui::KeyboardShortcut = + egui::KeyboardShortcut::new(egui::Modifiers::CTRL, egui::Key::R); +const PAUSE_SHORTCUT: egui::KeyboardShortcut = + egui::KeyboardShortcut::new(egui::Modifiers::CTRL, egui::Key::P); +const CLOSE_SHORTCUT: egui::KeyboardShortcut = + egui::KeyboardShortcut::new(egui::Modifiers::CTRL, egui::Key::Q); + +struct App { + fps: Fps, + nes: NES, + audio_player: AudioPlayer, + image_texture: egui::TextureHandle, + paused: bool, +} + +impl App { + pub fn new(ctx: &egui::Context, nes: NES) -> Self { + Self { + fps: Fps::new(TARGET_FPS), + nes, + audio_player: AudioPlayer::new(SAMPLE_RATE, dynwave::BufferSize::QuarterSecond) + .unwrap(), + paused: false, + image_texture: ctx.load_texture( + "nes-image", + egui::ColorImage::from_rgb( + [TV_WIDTH, TV_HEIGHT], + vec![0; TV_WIDTH * TV_HEIGHT * 3].as_slice(), + ), + egui::TextureOptions { + magnification: egui::TextureFilter::Nearest, + minification: egui::TextureFilter::Nearest, + ..Default::default() + }, + ), + } + } + + fn get_present_save_states(&self) -> Option> { + if self.nes.is_empty() { + return None; + } + + let base_saved_states_dir = base_save_state_folder()?; + + Some( + (MIN_STATE_SLOT..=MAX_STATE_SLOT) + .map(|i| { + let filename = self.nes.save_state_file_name(i).unwrap(); + + (i, base_saved_states_dir.join(&filename).exists()) + }) + .collect(), + ) + } + + fn save_state(&mut self, slot: u8) { + if self.nes.is_empty() { + return; + } + + let base_saved_states_dir = base_save_state_folder().unwrap(); + let filename = self.nes.save_state_file_name(slot).unwrap(); + let path = base_saved_states_dir.join(&filename); + + let file = fs::File::create(&path).unwrap(); + + self.nes.save_state(&file).unwrap(); + } + + fn load_state(&mut self, slot: u8) { + if self.nes.is_empty() { + return; + } + + let base_saved_states_dir = base_save_state_folder().unwrap(); + let filename = self.nes.save_state_file_name(slot).unwrap(); + let path = base_saved_states_dir.join(&filename); + + let file = fs::File::open(&path).unwrap(); + + self.nes.load_state(&file).unwrap(); + } + + fn handle_input(&mut self, ctx: &egui::Context) { + ctx.input_mut(|i| { + if !i.raw.dropped_files.is_empty() { + let file = i + .raw + .dropped_files + .iter() + .filter_map(|f| f.path.as_ref()) + .find(|f| f.extension().map(|e| e == "nes").unwrap_or(false)); + + if let Some(file) = file { + self.nes = NES::new(file).unwrap(); + } else { + // convert to error alert + println!("[ERROR] Dropped file is not a NES ROM, must have .nes extension"); + } + } + if !i.focused { + return; + } + + if i.consume_shortcut(&OPEN_SHORTCUT) { + self.open_file(); + } + if i.consume_shortcut(&RESET_SHORTCUT) { + self.nes.reset(); + } + if i.consume_shortcut(&PAUSE_SHORTCUT) { + self.paused = !self.paused; + if !self.paused { + // clear the audio buffer + _ = self.nes.audio_buffer(); + } + } + if i.consume_shortcut(&CLOSE_SHORTCUT) { + self.nes = NES::new_without_file(); + } + + if !self.nes.is_empty() { + self.nes + .controller() + .set_state(StandardNESKey::B, i.key_down(egui::Key::J)); + self.nes + .controller() + .set_state(StandardNESKey::A, i.key_down(egui::Key::K)); + self.nes + .controller() + .set_state(StandardNESKey::Select, i.key_down(egui::Key::U)); + self.nes + .controller() + .set_state(StandardNESKey::Start, i.key_down(egui::Key::I)); + self.nes + .controller() + .set_state(StandardNESKey::Up, i.key_down(egui::Key::W)); + self.nes + .controller() + .set_state(StandardNESKey::Down, i.key_down(egui::Key::S)); + self.nes + .controller() + .set_state(StandardNESKey::Left, i.key_down(egui::Key::A)); + self.nes + .controller() + .set_state(StandardNESKey::Right, i.key_down(egui::Key::D)); + } + }); + } + + fn update_title(&mut self, ctx: &egui::Context) { + let title = format!( + "Plastic {} {}", + if self.nes.is_empty() || self.paused { + "".to_owned() + } else { + format!("({:.0} FPS)", self.fps.fps()) + }, + if self.paused { "- Paused" } else { "" } + ); + + ctx.send_viewport_cmd(egui::ViewportCommand::Title(title)); + } + + fn open_file(&mut self) { + if let Some(file) = rfd::FileDialog::new() + .set_title("Open NES ROM") + .add_filter("NES ROM", &["nes"]) + .pick_file() + { + self.nes = NES::new(file).unwrap(); + } + } + + fn show_menu(&mut self, ui: &mut egui::Ui) { + egui::menu::bar(ui, |ui| { + ui.menu_button("File", |ui| { + if ui + .add( + egui::Button::new("Open") + .shortcut_text(ui.ctx().format_shortcut(&OPEN_SHORTCUT)), + ) + .clicked() + { + self.open_file(); + } + if ui + .add( + egui::Button::new("Reset") + .shortcut_text(ui.ctx().format_shortcut(&RESET_SHORTCUT)), + ) + .clicked() + { + self.nes.reset(); + } + if ui + .add( + egui::Button::new("Pause") + .selected(self.paused) + .shortcut_text(ui.ctx().format_shortcut(&PAUSE_SHORTCUT)), + ) + .clicked() + { + self.paused = !self.paused; + if !self.paused { + // clear the audio buffer + _ = self.nes.audio_buffer(); + } + } + if ui + .add( + egui::Button::new("Close") + .shortcut_text(ui.ctx().format_shortcut(&CLOSE_SHORTCUT)), + ) + .clicked() + { + self.nes = NES::new_without_file(); + } + if ui.button("Exit").clicked() { + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close); + } + }); + ui.menu_button("Save State", |ui| { + if let Some(slots) = self.get_present_save_states() { + for slot in slots { + if ui + .button(format!( + "Slot {} - {}", + slot.0, + if slot.1 { "Overwrite" } else { "Save" } + )) + .clicked() + { + self.save_state(slot.0); + } + } + } + }); + ui.menu_button("Load State", |ui| { + if let Some(slots) = self.get_present_save_states() { + for slot in slots { + if ui + .add_enabled(slot.1, egui::Button::new(format!("Slot {}", slot.0))) + .clicked() + && slot.1 + { + self.load_state(slot.0); + } + } + } + }); + ui.menu_button("Speed", |ui| { + let mut speed = self.fps.target_fps / TARGET_FPS; + ui.add( + egui::Slider::new(&mut speed, 0.1..=10.0) + .text("Emulation Speed") + .clamp_to_range(true), + ); + self.fps.target_fps = TARGET_FPS * speed; + }); + }); + } + + /// Schedule the update so that the frame rate is capped at the target fps + fn schedule_update(&mut self, ctx: &egui::Context) { + if let Some(remaining) = self.fps.remaining() { + ctx.request_repaint_after(remaining); + } else { + ctx.request_repaint(); + } + } +} + +impl eframe::App for App { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + self.update_title(ctx); + self.handle_input(ctx); + + if !self.paused && !self.nes.is_empty() { + if self.fps.start_frame() { + self.nes.clock_for_frame(); + let audio_buffer = self.nes.audio_buffer(); + self.audio_player.queue(&process_audio( + &audio_buffer, + (TARGET_FPS / self.fps.target_fps) as f32, + )); + } + self.audio_player.play().unwrap(); + } else { + self.audio_player.pause().unwrap(); + } + + egui::CentralPanel::default().show(ctx, |ui| { + self.show_menu(ui); + ui.centered_and_justified(|ui| { + if !self.nes.is_empty() { + { + self.image_texture.set( + egui::ColorImage::from_rgb( + [TV_WIDTH, TV_HEIGHT], + self.nes.pixel_buffer(), + ), + egui::TextureOptions { + magnification: egui::TextureFilter::Nearest, + minification: egui::TextureFilter::Nearest, + ..Default::default() + }, + ); + } + + let rect = ui.available_rect_before_wrap(); + + // image + ui.add( + egui::Image::from_texture(&self.image_texture) + .maintain_aspect_ratio(true) + .shrink_to_fit(), + ); + + // the pause indicator + if self.paused { + let center = rect.center(); + let offset = 40.0; + let right_rect = egui::Rect::from_min_max( + center + egui::vec2(offset, -offset * 2.), + center + egui::vec2(offset + 40.0, offset * 2.), + ); + let left_rect = egui::Rect::from_min_max( + center + egui::vec2(-offset - 40.0, -offset * 2.), + center + egui::vec2(-offset, offset * 2.), + ); + + ui.painter().rect_filled( + right_rect, + 3.0, + egui::Color32::from_black_alpha(200), + ); + ui.painter().rect_filled( + left_rect, + 3.0, + egui::Color32::from_black_alpha(200), + ); + } + } else { + ui.label("No game loaded"); + } + }); + }); + + self.schedule_update(ctx); + } +} + +pub fn main() -> Result<(), eframe::Error> { + let file = std::env::args().nth(1); + let nes = match file { + Some(file) => NES::new(&file).unwrap(), + None => NES::new_without_file(), + }; + + eframe::run_native( + "Plastic", + eframe::NativeOptions { + window_builder: Some(Box::new(|builder| { + builder.with_drag_and_drop(true).with_icon( + eframe::icon_data::from_png_bytes(include_bytes!("../../images/icon.png")) + .unwrap(), + ) + })), + vsync: false, // unlock FPS + ..Default::default() + }, + Box::new(|c| Ok(Box::new(App::new(&c.egui_ctx, nes)))), + ) +} diff --git a/nes_ui_sfml/Cargo.toml b/plastic_ui_tui/Cargo.toml similarity index 72% rename from nes_ui_sfml/Cargo.toml rename to plastic_ui_tui/Cargo.toml index a92b5ad..7f7bfb9 100644 --- a/nes_ui_sfml/Cargo.toml +++ b/plastic_ui_tui/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nes_ui_sfml" +name = "plastic_ui_tui" version = "0.1.0" authors = ["Amjad Alsharafi "] edition = "2021" @@ -9,4 +9,7 @@ edition = "2021" [dependencies] plastic_core = { path = "../plastic_core" } -sfml = "^0.15.1" +crossterm = "0.28" +gilrs = "^0.7.4" +dynwave = "0.1.0" +ratatui = "0.28.1" diff --git a/plastic_ui_tui/src/main.rs b/plastic_ui_tui/src/main.rs new file mode 100644 index 0000000..d1ea10c --- /dev/null +++ b/plastic_ui_tui/src/main.rs @@ -0,0 +1,24 @@ +mod ui; +use plastic_core::nes::NES; +use std::env::args; + +fn main() { + let args = args().collect::>(); + + if args.len() < 2 { + eprintln!("USAGE: {} [-a]\n-a: remove audio", args[0]); + return; + } + + let nes = match NES::new(&args[1]) { + Ok(nes) => nes, + Err(e) => { + eprintln!("Error: {}", e); + return; + } + }; + + let has_audio = !(args.len() == 3 && args[2] == "-a"); + + ui::Ui::new(nes, has_audio).run(); +} diff --git a/plastic_ui_tui/src/ui.rs b/plastic_ui_tui/src/ui.rs new file mode 100644 index 0000000..1aa6d32 --- /dev/null +++ b/plastic_ui_tui/src/ui.rs @@ -0,0 +1,276 @@ +use dynwave::AudioPlayer; +use plastic_core::{ + misc::{process_audio, Fps}, + nes::NES, + nes_audio::SAMPLE_RATE, + nes_controller::StandardNESKey, + nes_display::{TV_HEIGHT, TV_WIDTH}, +}; +use ratatui::{ + prelude::*, + style::Color, + widgets::{ + block::Title, + canvas::{Canvas, Painter, Shape}, + Block, Borders, + }, +}; +use std::{collections::HashMap, thread}; +use std::{io, time::Duration}; +use symbols::Marker; + +use gilrs::{Button, Event as GilrsEvent, EventType, Gilrs}; + +use crossterm::{ + cursor::{Hide, Show}, + event::{ + Event, KeyCode, KeyEventKind, KeyModifiers, KeyboardEnhancementFlags, + PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, + }, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, +}; + +struct ImageView<'a> { + image: &'a [u8], +} + +impl Shape for ImageView<'_> { + fn draw(&self, painter: &mut Painter) { + for x in 0..TV_WIDTH { + for y in 0..TV_HEIGHT { + let index = (TV_HEIGHT - y - 1) * TV_WIDTH + x; + if let Some((x, y)) = painter.get_point(x as f64, y as f64) { + let r = self.image[index * 3]; + let g = self.image[index * 3 + 1]; + let b = self.image[index * 3 + 2]; + painter.paint(x, y, Color::Rgb(r, g, b)); + } + } + } + } +} + +pub struct Ui { + pub nes: NES, + + audio_player: Option>, + gilrs: Gilrs, + active_gamepad: Option, + + /// For terminals without support for `Release` key event, we keep the button pressed for some + /// time + keyboard_event_counter: HashMap, +} + +impl Ui { + pub fn new(nes: NES, has_audio: bool) -> Self { + Ui { + nes, + + audio_player: if has_audio { + Some(AudioPlayer::new(SAMPLE_RATE, dynwave::BufferSize::QuarterSecond).unwrap()) + } else { + None + }, + gilrs: Gilrs::new().unwrap(), + keyboard_event_counter: HashMap::new(), + active_gamepad: None, + } + } + + fn display(&mut self, terminal: &mut Terminal, fps: &Fps) { + terminal + .draw(move |f| { + let block = Block::default() + .borders(Borders::ALL) + .title(Title::from("Plastic").alignment(Alignment::Center)) + .title( + Title::from(format!("(FPS: {:.2})", fps.fps())).alignment(Alignment::Left), + ) + .title( + Title::from(format!( + "(Terminal size: {}x{})", + f.area().width, + f.area().height + )) + .alignment(Alignment::Right), + ) + .title_style(Style::default().bold().fg(Color::Yellow)); + let canvas = Canvas::default() + .block(block) + .x_bounds([0., TV_WIDTH as f64]) + .y_bounds([0., TV_HEIGHT as f64]) + .marker(Marker::HalfBlock) + .paint(|ctx| { + ctx.draw(&ImageView { + image: self.nes.pixel_buffer(), + }); + }); + f.render_widget(canvas, f.area()); + }) + .unwrap(); + } + + fn handle_keyboard(&mut self, has_keyboard_enhancement: bool) -> bool { + // read them in + while let Ok(has_event) = crossterm::event::poll(Duration::from_millis(5)) { + if !has_event { + break; + } + let Ok(event) = crossterm::event::read() else { + break; + }; + match event { + Event::Key(input) => { + let modifiers = input.modifiers; + let code = input.code; + let possible_button = match code { + KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => return true, + + KeyCode::Char('C') | KeyCode::Char('c') + if modifiers.intersects(KeyModifiers::CONTROL) => + { + return true + } + KeyCode::Char('R') | KeyCode::Char('r') + if modifiers.intersects(KeyModifiers::CONTROL) => + { + self.nes.reset(); + None + } + KeyCode::Char('J') | KeyCode::Char('j') => Some(StandardNESKey::B), + KeyCode::Char('K') | KeyCode::Char('k') => Some(StandardNESKey::A), + KeyCode::Char('U') | KeyCode::Char('u') => Some(StandardNESKey::Select), + KeyCode::Char('I') | KeyCode::Char('i') => Some(StandardNESKey::Start), + KeyCode::Char('W') | KeyCode::Char('w') => Some(StandardNESKey::Up), + KeyCode::Char('S') | KeyCode::Char('s') => Some(StandardNESKey::Down), + KeyCode::Char('A') | KeyCode::Char('a') => Some(StandardNESKey::Left), + KeyCode::Char('D') | KeyCode::Char('d') => Some(StandardNESKey::Right), + _ => None, + }; + if let Some(button) = possible_button { + match input.kind { + KeyEventKind::Press | KeyEventKind::Repeat => { + self.nes.controller().set_state(button, true); + if !has_keyboard_enhancement { + // 20 frames + // TODO: very arbitrary, but it works on some of the games + // tested + self.keyboard_event_counter.insert(button, 20); + } + } + KeyEventKind::Release => { + self.nes.controller().set_state(button, false); + } + } + } + } + _ => {} + } + } + + // decrement the counter for the keys that are being held + if !has_keyboard_enhancement { + self.keyboard_event_counter + .iter_mut() + .for_each(|(_, counter)| { + *counter = counter.saturating_sub(1); + }); + + self.keyboard_event_counter.retain(|key, counter| { + if *counter == 0 { + self.nes.controller().set_state(*key, false); + false + } else { + true + } + }); + } + + false + } + + fn handle_gamepad(&mut self) { + // set events in the cache and check if gamepad is still active + while let Some(GilrsEvent { id, event, .. }) = self.gilrs.next_event() { + self.active_gamepad = Some(id); + if event == EventType::Disconnected { + self.active_gamepad = None; + } + } + + if let Some(gamepad) = self.active_gamepad.map(|id| self.gilrs.gamepad(id)) { + for (controller_button, nes_button) in &[ + (Button::South, StandardNESKey::B), + (Button::East, StandardNESKey::A), + (Button::Select, StandardNESKey::Select), + (Button::Start, StandardNESKey::Start), + (Button::DPadUp, StandardNESKey::Up), + (Button::DPadDown, StandardNESKey::Down), + (Button::DPadRight, StandardNESKey::Right), + (Button::DPadLeft, StandardNESKey::Left), + ] { + if gamepad.is_pressed(*controller_button) { + self.nes.controller().set_state(*nes_button, true); + } else { + self.nes.controller().set_state(*nes_button, false); + } + } + } + } + + pub fn run(&mut self) { + let mut stdout = io::stdout(); + + execute!( + stdout, + EnterAlternateScreen, + Hide, + PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::REPORT_EVENT_TYPES), + ) + .unwrap(); + enable_raw_mode().unwrap(); + + let has_keyboard_enhancement = + crossterm::terminal::supports_keyboard_enhancement().unwrap(); + + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend).unwrap(); + let mut fps = Fps::new(61.0); + + if let Some(ref mut player) = self.audio_player { + player.play().unwrap(); + } + + loop { + fps.start_frame(); + if self.handle_keyboard(has_keyboard_enhancement) { + break; + } + + self.handle_gamepad(); + + self.nes.clock_for_frame(); + self.display(&mut terminal, &fps); + + if let Some(ref mut player) = self.audio_player { + let audio_buffer = process_audio(&self.nes.audio_buffer(), 1.0); + player.queue(&audio_buffer); + } + + if let Some(remaining) = fps.remaining() { + thread::sleep(remaining); + } + } + + disable_raw_mode().unwrap(); + execute!( + io::stdout(), + Show, + LeaveAlternateScreen, + PopKeyboardEnhancementFlags, + ) + .unwrap(); + } +}