commit 23bdc65da38df5469dc1ae02f06cf5d1b59b8b5d Author: not-elm Date: Sun Aug 10 21:28:45 2025 +0900 INIT diff --git a/.claude/commands/create-test.md b/.claude/commands/create-test.md new file mode 100644 index 0000000..88a2b31 --- /dev/null +++ b/.claude/commands/create-test.md @@ -0,0 +1,39 @@ +--- +allowed-tools: Bash(cargo test:*), +description: Create new tests +--- + +1. Reading $ARGUMENTS and understanding the implementation details. +2. Creating new tests for $ARGUMENTS in `tests` module in the same file. +3. Run `cargo test --workspace --all-features` to ensure all tests pass. +4. If any tests fail, fix the issues and re-run the tests. + +## Contracts + +- You have to write the rust-doc that describes the test case for each test function. + + +以下の手順で問題を修正してください。 + +## 現在発生している問題 + +以下のようなHTMLにCEFのカスタムリソースハンドラを使って`cef://localhost/brp.html`経由でアクセスしています。 +このHTMLからvideoリソースを読み込むと、`cef://localhost/test.mov`というリクエストURLでローカルResourceHandlerのopenメソッドが呼ばれ、response_headers, readメソッドでヘッダーとレスポンスボディが返されることが期待されます。 +初回と2回目までのreadメソッドは呼ばれるのですが、3回目以降のreadメソッドが呼ばれず、何度もopenメソッドが呼ばれてしまっているため、正常にレスポンスが返却できるように修正してください。 + +```htm + + + + + +``` + +## 手順 + +1. [CEFのResourceHandler](https://cef-builds.spotifycdn.com/docs/122.0/classCefResourceHandler.html)の仕様を深く読み込み理解する +2. `crates/bevy_cef_core/src/browser_process/localhost.rs`以下にResourceHandlerを使ってローカルリソースを読み込むコードが書いています。深く読み込んで現状の実装を理解してください。 +3. openメソッドが複数回呼ばれ、readメソッドが3回目以降呼ばれない原因を調査する +4. 原因を特定し、修正する \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..c7919fb --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,7 @@ + +{ + "permissions": { + "allow": ["WebFetch", "WebSearch"], + "deny": ["Read(./.env)", "Read(./secrets/**)"] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5535a2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +.idea/ +book/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5216bc6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,102 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is `bevy_cef`, a Bevy plugin that integrates the Chromium Embedded Framework (CEF) into Bevy applications, allowing webviews to be rendered as 3D objects in the game world or as UI overlays. + +## Architecture + +### Multi-Process Design +- **Browser Process**: Main application process running Bevy (`bevy_cef_core::browser_process`) +- **Render Process**: Separate CEF render process (`bevy_cef_core::render_process`) +- Communication through IPC channels and Bevy Remote Protocol (BRP) + +### Core Components +- `CefWebviewUri`: Component specifying webview URL (remote or local via `cef://localhost/`) +- `WebviewSize`: Controls webview rendering dimensions (default 800x800) +- `WebviewExtendStandardMaterial`: Material for rendering webviews on 3D meshes +- `HostWindow`: Optional parent window specification +- `ZoomLevel`: Webview zoom control +- `AudioMuted`: Audio muting control + +### Plugin Architecture +The main `CefPlugin` orchestrates several sub-plugins: +- `LocalHostPlugin`: Serves local assets via custom scheme +- `MessageLoopPlugin`: CEF message loop integration +- `WebviewCoreComponentsPlugin`: Core component registration +- `WebviewPlugin`: Main webview management +- `IpcPlugin`: Inter-process communication +- `KeyboardPlugin`, `NavigationPlugin`, `ZoomPlugin`, `AudioMutePlugin`: Feature-specific functionality + +### IPC System +Three communication patterns: +1. **JS Emit**: Webview → Bevy app via `JsEmitEventPlugin` +2. **Host Emit**: Bevy app → Webview via event emission +3. **BRP (Bevy Remote Protocol)**: Bidirectional RPC calls + +## Development Commands + +### Code Quality +```bash +# Fix and format code +make fix +# Which runs: +# cargo clippy --fix --allow-dirty --allow-staged --workspace --all --all-features +# cargo fmt --all +``` + +### Development Setup +The build system automatically handles CEF dependencies on macOS with debug feature: +- Installs `bevy_cef_debug_render_process` tool +- Installs `export-cef-dir` tool +- Downloads/extracts CEF framework to `$HOME/.local/share/cef` + +### Manual Installation +```bash +# Install debug render process tool +make install +# Or: cargo install --path ./crates/bevy_cef_debug_render_process --force +``` + +## Key Examples + +- `examples/simple.rs`: Basic webview on 3D plane +- `examples/js_emit.rs`: JavaScript to Bevy communication +- `examples/host_emit.rs`: Bevy to JavaScript communication +- `examples/brp.rs`: Bidirectional RPC with devtools +- `examples/navigation.rs`: Page navigation controls +- `examples/zoom_level.rs`: Zoom functionality +- `examples/sprite.rs`: Webview as 2D sprite +- `examples/devtool.rs`: Chrome DevTools integration + +## Local Asset Loading + +Local HTML/assets are served via the custom `cef://localhost/` scheme: +- Place assets in `assets/` directory +- Reference as `CefWebviewUri::local("filename.html")` +- Or manually: `cef://localhost/filename.html` + +## Testing + +No automated tests are present in this codebase. Testing is done through the example applications. + +### Manually Testing + +- Run tests with `cargo test --workspace --all-features` + +## Platform Notes + +- Currently focused on macOS development (see Cargo.toml target-specific dependencies) +- CEF framework must be available at `$HOME/.local/share/cef` +- Uses `objc` crate for macOS-specific window handling +- DYLD environment variables required for CEF library loading + +## Workspace Structure + +This is a Cargo workspace with: +- Root crate: `bevy_cef` (public API) +- `crates/bevy_cef_core`: Core CEF integration logic +- `crates/bevy_cef_debug_render_process`: Debug render process executable +- `examples/demo`: Standalone demo application \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1cdf910 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6276 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "accesskit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "becf0eb5215b6ecb0a739c31c21bd83c4f326524c9b46b7e882d77559b60a529" +dependencies = [ + "enumn", + "serde", +] + +[[package]] +name = "accesskit_consumer" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0bf66a7bf0b7ea4fd7742d50b64782a88f99217cf246b3f93b4162528dde520" +dependencies = [ + "accesskit", + "hashbrown", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e230718177753b4e4ad9e1d9f6cfc2f4921212d4c1c480b253f526babb258d" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "accesskit_windows" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65178f3df98a51e4238e584fcb255cb1a4f9111820848eeddd37663be40a625f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown", + "paste", + "static_assertions", + "windows 0.58.0", + "windows-core 0.58.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d941bb8c414caba6e206de669c7dc0dbeb305640ea890772ee422a40e6b89f" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_windows", + "raw-window-handle", + "winit", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.9.1", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.9.1", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk 0.9.0", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "assert_type_match" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.0.8", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "atomicow" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52e8890bb9844440d0c412fa74b67fd2f14e85248b6e00708059b6da9e5f8bf" +dependencies = [ + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bevy" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8369c16b7c017437021341521f8b4a0d98e1c70113fb358c3258ae7d661d79" +dependencies = [ + "bevy_internal", +] + +[[package]] +name = "bevy_a11y" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3561712cf49074d89e9989bfc2e6c6add5d33288f689db9a0c333300d2d004" +dependencies = [ + "accesskit", + "bevy_app", + "bevy_derive", + "bevy_ecs", + "bevy_reflect", + "serde", +] + +[[package]] +name = "bevy_animation" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49796627726d0b9a722ad9a0127719e7c1868f474d6575ec0f411e8299c4d7bb" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_derive", + "bevy_ecs", + "bevy_log", + "bevy_math", + "bevy_mesh", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_time", + "bevy_transform", + "bevy_utils", + "blake3", + "derive_more", + "downcast-rs", + "either", + "petgraph", + "ron", + "serde", + "smallvec", + "thiserror 2.0.12", + "thread_local", + "tracing", + "uuid", +] + +[[package]] +name = "bevy_app" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4491cc4c718ae76b4c6883df58b94cc88b32dcd894ea8d5b603c7c7da72ca967" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "cfg-if", + "console_error_panic_hook", + "ctrlc", + "downcast-rs", + "log", + "thiserror 2.0.12", + "variadics_please", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "bevy_asset" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56111d9b88d8649f331a667d9d72163fb26bd09518ca16476d238653823db1e" +dependencies = [ + "async-broadcast", + "async-fs", + "async-lock", + "atomicow", + "bevy_app", + "bevy_asset_macros", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_window", + "bitflags 2.9.1", + "blake3", + "crossbeam-channel", + "derive_more", + "disqualified", + "downcast-rs", + "either", + "futures-io", + "futures-lite", + "js-sys", + "notify-debouncer-full", + "parking_lot", + "ron", + "serde", + "stackfuture", + "thiserror 2.0.12", + "tracing", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "bevy_asset_macros" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4cca3e67c0ec760d8889d42293d987ce5da92eaf9c592bf5d503728a63b276d" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_audio" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b4f6f2a5c6c0e7c6825e791d2a061c76c2d6784f114c8f24382163fabbfaaa" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_transform", + "cpal", + "rodio", + "tracing", +] + +[[package]] +name = "bevy_cef" +version = "0.1.0" +dependencies = [ + "async-channel", + "bevy", + "bevy_cef", + "bevy_cef_core", + "bevy_remote", + "cef", + "objc", + "raw-window-handle", + "serde", + "serde_json", +] + +[[package]] +name = "bevy_cef_core" +version = "0.1.0" +dependencies = [ + "async-channel", + "bevy", + "bevy_remote", + "cef", + "cef-dll-sys", + "raw-window-handle", + "serde", + "serde_json", + "uuid", +] + +[[package]] +name = "bevy_cef_debug_render_process" +version = "0.1.0" +dependencies = [ + "bevy_cef_core", + "cef", + "libloading", +] + +[[package]] +name = "bevy_color" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c101cbe1e26b8d701eb77263b14346e2e0cbbd2a6e254b9b1aead814e5ca8d3" +dependencies = [ + "bevy_math", + "bevy_reflect", + "bytemuck", + "derive_more", + "encase", + "serde", + "thiserror 2.0.12", + "wgpu-types", +] + +[[package]] +name = "bevy_core_pipeline" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed46363cad80dc00f08254c3015232bd6f640738403961c6d63e7ecfc61625" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_image", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.9.1", + "bytemuck", + "nonmax", + "radsort", + "serde", + "smallvec", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "bevy_derive" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b837bf6c51806b10ebfa9edf1844ad80a3a0760d6c5fac4e90761df91a8901a" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + +[[package]] +name = "bevy_diagnostic" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48797366f312a8f31e237d08ce3ee70162591282d2bfe7c5ad8be196fb263e55" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_tasks", + "bevy_time", + "bevy_utils", + "const-fnv1a-hash", + "log", + "serde", + "sysinfo", +] + +[[package]] +name = "bevy_ecs" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2bf6521aae57a0ec3487c4bfb59e36c4a378e834b626a4bea6a885af2fdfe7" +dependencies = [ + "arrayvec", + "bevy_ecs_macros", + "bevy_platform", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bitflags 2.9.1", + "bumpalo", + "concurrent-queue", + "derive_more", + "disqualified", + "fixedbitset", + "indexmap", + "log", + "nonmax", + "serde", + "smallvec", + "thiserror 2.0.12", + "variadics_please", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38748d6f3339175c582d751f410fb60a93baf2286c3deb7efebb0878dce7f413" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_encase_derive" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8148f4edee470a2ea5cad010184c492a4c94c36d7a7158ea28e134ea87f274ab" +dependencies = [ + "bevy_macro_utils", + "encase_derive_impl", +] + +[[package]] +name = "bevy_gilrs" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97efef87c631949e67d06bb5d7dfd2a5f936b3b379afb6b1485b08edbb219b87" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_platform", + "bevy_time", + "bevy_utils", + "gilrs", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "bevy_gizmos" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7823154a9682128c261d8bddb3a4d7192a188490075c527af04520c2f0f8aad6" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_gizmos_macros", + "bevy_image", + "bevy_math", + "bevy_pbr", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bytemuck", + "tracing", +] + +[[package]] +name = "bevy_gizmos_macros" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f378f3b513218ddc78254bbe76536d9de59c1429ebd0c14f5d8f2a25812131ad" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_gltf" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a080237c0b8842ccc15a06d3379302c68580eeea4497b1c7387e470eda1f07" +dependencies = [ + "base64 0.22.1", + "bevy_animation", + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_image", + "bevy_math", + "bevy_mesh", + "bevy_pbr", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_scene", + "bevy_tasks", + "bevy_transform", + "bevy_utils", + "fixedbitset", + "gltf", + "itertools 0.14.0", + "percent-encoding", + "serde", + "serde_json", + "smallvec", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "bevy_image" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e6e900cfecadbc3149953169e36b9e26f922ed8b002d62339d8a9dc6129328" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_utils", + "bitflags 2.9.1", + "bytemuck", + "futures-lite", + "guillotiere", + "half", + "image", + "ktx2", + "rectangle-pack", + "ruzstd", + "serde", + "thiserror 2.0.12", + "tracing", + "wgpu-types", +] + +[[package]] +name = "bevy_input" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d6b6516433f6f7d680f648d04eb1866bb3927a1782d52f74831b62042f3cd1" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_utils", + "derive_more", + "log", + "serde", + "smol_str", + "thiserror 2.0.12", +] + +[[package]] +name = "bevy_input_focus" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e2d079fda74d1416e0a57dac29ea2b79ff77f420cd6b87f833d3aa29a46bc4d" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_reflect", + "bevy_window", + "log", + "thiserror 2.0.12", +] + +[[package]] +name = "bevy_internal" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "857da8785678fde537d02944cd20dec9cafb7d4c447efe15f898dc60e733cacd" +dependencies = [ + "bevy_a11y", + "bevy_animation", + "bevy_app", + "bevy_asset", + "bevy_audio", + "bevy_color", + "bevy_core_pipeline", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_gilrs", + "bevy_gizmos", + "bevy_gltf", + "bevy_image", + "bevy_input", + "bevy_input_focus", + "bevy_log", + "bevy_math", + "bevy_pbr", + "bevy_picking", + "bevy_platform", + "bevy_ptr", + "bevy_reflect", + "bevy_render", + "bevy_scene", + "bevy_sprite", + "bevy_state", + "bevy_tasks", + "bevy_text", + "bevy_time", + "bevy_transform", + "bevy_ui", + "bevy_utils", + "bevy_window", + "bevy_winit", +] + +[[package]] +name = "bevy_log" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a61ee8aef17a974f5ca481dcedf0c2bd52670e231d4c4bc9ddef58328865f9" +dependencies = [ + "android_log-sys", + "bevy_app", + "bevy_ecs", + "bevy_utils", + "tracing", + "tracing-log", + "tracing-oslog", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052eeebcb8e7e072beea5031b227d9a290f8a7fbbb947573ab6ec81df0fb94be" +dependencies = [ + "parking_lot", + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_math" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68553e0090fe9c3ba066c65629f636bd58e4ebd9444fdba097b91af6cd3e243f" +dependencies = [ + "approx", + "bevy_reflect", + "derive_more", + "glam", + "itertools 0.14.0", + "libm", + "rand", + "rand_distr", + "serde", + "smallvec", + "thiserror 2.0.12", + "variadics_please", +] + +[[package]] +name = "bevy_mesh" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10399c7027001edbc0406d7d0198596b1f07206c1aae715274106ba5bdcac40" +dependencies = [ + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_image", + "bevy_math", + "bevy_mikktspace", + "bevy_platform", + "bevy_reflect", + "bevy_transform", + "bevy_utils", + "bitflags 2.9.1", + "bytemuck", + "hexasphere", + "serde", + "thiserror 2.0.12", + "tracing", + "wgpu-types", +] + +[[package]] +name = "bevy_mikktspace" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb60c753b968a2de0fd279b76a3d19517695e771edb4c23575c7f92156315de" +dependencies = [ + "glam", +] + +[[package]] +name = "bevy_pbr" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5e0b4eb871f364a0d217f70f6c41d7fdc6f9f931fa1abbf222180c03d0ae410" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_image", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.9.1", + "bytemuck", + "derive_more", + "fixedbitset", + "nonmax", + "offset-allocator", + "radsort", + "smallvec", + "static_assertions", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "bevy_picking" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed04757938655ed8094ea1efb533f99063a8b22abffc22010c694d291522850" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_mesh", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bevy_window", + "crossbeam-channel", + "tracing", + "uuid", +] + +[[package]] +name = "bevy_platform" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7573dc824a1b08b4c93fdbe421c53e1e8188e9ca1dd74a414455fe571facb47" +dependencies = [ + "cfg-if", + "critical-section", + "foldhash", + "getrandom 0.2.16", + "hashbrown", + "portable-atomic", + "portable-atomic-util", + "serde", + "spin", + "web-time", +] + +[[package]] +name = "bevy_ptr" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7370d0e46b60e071917711d0860721f5347bc958bf325975ae6913a5dfcf01" + +[[package]] +name = "bevy_reflect" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeb91a63a1a4df00aa58da8cc4ddbd4b9f16ab8bb647c5553eb156ce36fa8c2" +dependencies = [ + "assert_type_match", + "bevy_platform", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "derive_more", + "disqualified", + "downcast-rs", + "erased-serde", + "foldhash", + "glam", + "petgraph", + "serde", + "smallvec", + "smol_str", + "thiserror 2.0.12", + "uuid", + "variadics_please", + "wgpu-types", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ddadc55fe16b45faaa54ab2f9cb00548013c74812e8b018aa172387103cce6" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_remote" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d073f6104c48d7c0468f682b3b2a00ee709001c3cf2778e0555afdb1305eb0" +dependencies = [ + "anyhow", + "async-channel", + "async-io", + "bevy_app", + "bevy_derive", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "http-body-util", + "hyper", + "serde", + "serde_json", + "smol-hyper", +] + +[[package]] +name = "bevy_render" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef91fed1f09405769214b99ebe4390d69c1af5cdd27967deae9135c550eb1667" +dependencies = [ + "async-channel", + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_encase_derive", + "bevy_image", + "bevy_math", + "bevy_mesh", + "bevy_platform", + "bevy_reflect", + "bevy_render_macros", + "bevy_tasks", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.9.1", + "bytemuck", + "codespan-reporting", + "derive_more", + "downcast-rs", + "encase", + "fixedbitset", + "futures-lite", + "image", + "indexmap", + "js-sys", + "ktx2", + "naga", + "naga_oil", + "nonmax", + "offset-allocator", + "send_wrapper", + "serde", + "smallvec", + "thiserror 2.0.12", + "tracing", + "variadics_please", + "wasm-bindgen", + "web-sys", + "wgpu", +] + +[[package]] +name = "bevy_render_macros" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd42cf6c875bcf38da859f8e731e119a6aff190d41dd0a1b6000ad57cf2ed3d" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_scene" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c52ca165200995fe8afd2a1a6c03e4ffee49198a1d4653d32240ea7f217d4ab" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "derive_more", + "serde", + "thiserror 2.0.12", + "uuid", +] + +[[package]] +name = "bevy_sprite" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ccae7bab2cb956fb0434004c359e432a3a1a074a6ef4eb471f1fb099f0b620b" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_image", + "bevy_math", + "bevy_picking", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.9.1", + "bytemuck", + "derive_more", + "fixedbitset", + "nonmax", + "radsort", + "tracing", +] + +[[package]] +name = "bevy_state" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155d3cd97b900539008cdcaa702f88b724d94b08977b8e591a32536ce66faa8c" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_state_macros", + "bevy_utils", + "log", + "variadics_please", +] + +[[package]] +name = "bevy_state_macros" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2481c1304fd2a1851a0d4cb63a1ce6421ae40f3f0117cbc9882963ee4c9bb609" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_tasks" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b674242641cab680688fc3b850243b351c1af49d4f3417a576debd6cca8dcf5" +dependencies = [ + "async-channel", + "async-executor", + "async-task", + "atomic-waker", + "bevy_platform", + "cfg-if", + "concurrent-queue", + "crossbeam-queue", + "derive_more", + "futures-channel", + "futures-lite", + "heapless", + "pin-project", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_text" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d76c85366159f5f54110f33321c76d8429cfd8f39638f26793a305dae568b60" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_derive", + "bevy_ecs", + "bevy_image", + "bevy_log", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_transform", + "bevy_utils", + "bevy_window", + "cosmic-text", + "serde", + "smallvec", + "sys-locale", + "thiserror 2.0.12", + "tracing", + "unicode-bidi", +] + +[[package]] +name = "bevy_time" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc98eb356c75be04fbbc77bb3d8ffa24c8bacd99f76111cee23d444be6ac8c9c" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "crossbeam-channel", + "log", + "serde", +] + +[[package]] +name = "bevy_transform" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df218e440bb9a19058e1b80a68a031c887bcf7bd3a145b55f361359a2fa3100d" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_log", + "bevy_math", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "derive_more", + "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "bevy_ui" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a4d2ba51865bc3039af29a26b4f52c48b54cc758369f52004caf4b6f03770" +dependencies = [ + "accesskit", + "bevy_a11y", + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_image", + "bevy_input", + "bevy_math", + "bevy_picking", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_text", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bytemuck", + "derive_more", + "nonmax", + "serde", + "smallvec", + "taffy", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "bevy_utils" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f7a8905a125d2017e8561beefb7f2f5e67e93ff6324f072ad87c5fd6ec3b99" +dependencies = [ + "bevy_platform", + "thread_local", +] + +[[package]] +name = "bevy_window" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7e8ad0c17c3cc23ff5566ae2905c255e6986037fb041f74c446216f5c38431" +dependencies = [ + "android-activity", + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_utils", + "log", + "raw-window-handle", + "serde", + "smol_str", +] + +[[package]] +name = "bevy_winit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5e7f00c6b3b6823df5ec2a5e9067273607208919bc8c211773ebb9643c87f0" +dependencies = [ + "accesskit", + "accesskit_winit", + "approx", + "bevy_a11y", + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_image", + "bevy_input", + "bevy_input_focus", + "bevy_log", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_window", + "bytemuck", + "cfg-if", + "crossbeam-channel", + "raw-window-handle", + "serde", + "tracing", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winit", +] + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", +] + +[[package]] +name = "bindgen" +version = "0.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] + +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "bzip2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.9.1", + "log", + "polling", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "cc" +version = "1.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cef" +version = "138.8.0+138.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcab070ca301bdcc6e27d38f00840dafb241701270ddd4648c182cab0b0fbdca" +dependencies = [ + "cef-dll-sys", + "libloading", + "windows-sys 0.60.2", +] + +[[package]] +name = "cef-dll-sys" +version = "138.8.0+138.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c169c1f879d08df993756c21f6f24f0b14795e92eedab57c13c94c727cdcbe" +dependencies = [ + "anyhow", + "cmake", + "download-cef", + "serde_json", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width 0.1.14", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", + "portable-atomic", +] + +[[package]] +name = "console" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.1", + "windows-sys 0.60.2", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-fnv1a-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" + +[[package]] +name = "const_panic" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98d1483e98c9d67f341ab4b3915cfdc54740bd6f5cccc9226ee0535d86aa8fb" + +[[package]] +name = "const_soft_float" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "constgebra" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1aaf9b65849a68662ac6c0810c8893a765c960b907dd7cfab9c4a50bf764fbc" +dependencies = [ + "const_soft_float", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "coreaudio-rs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +dependencies = [ + "bitflags 1.3.2", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6" +dependencies = [ + "bindgen 0.72.0", +] + +[[package]] +name = "cosmic-text" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e418dd4f5128c3e93eab12246391c54a20c496811131f85754dc8152ee207892" +dependencies = [ + "bitflags 2.9.1", + "fontdb", + "log", + "rangemap", + "rustc-hash 1.1.0", + "rustybuzz", + "self_cell", + "smol_str", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cpal" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk 0.8.0", + "ndk-context", + "oboe", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.54.0", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "ctrlc" +version = "3.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix 0.30.1", + "windows-sys 0.59.0", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "demo" +version = "0.1.0" +dependencies = [ + "bevy", + "bevy_cef", + "bevy_remote", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "disqualified" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" + +[[package]] +name = "download-cef" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97f3e5e9cb37f5ccfbaaa66531a1b0e3bcaffa3e6c05e3412fffa2c25331531e" +dependencies = [ + "bzip2", + "clap", + "indicatif", + "regex", + "semver", + "serde", + "serde_json", + "sha1_smol", + "tar", + "thiserror 2.0.12", + "ureq", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encase" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a05902cf601ed11d564128448097b98ebe3c6574bd7b6a653a3d56d54aa020" +dependencies = [ + "const_panic", + "encase_derive", + "glam", + "thiserror 1.0.69", +] + +[[package]] +name = "encase_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "181d475b694e2dd56ae919ce7699d344d1fd259292d590c723a50d1189a2ea85" +dependencies = [ + "encase_derive_impl", +] + +[[package]] +name = "encase_derive_impl" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97b51c5cc57ef7c5f7a0c57c250251c49ee4c28f819f87ac32f4aceabc36792" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "file-id" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc6a637b6dc58414714eddd9170ff187ecb0933d4c7024d1abbd23a3cc26e9" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "font-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gilrs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb2c998745a3c1ac90f64f4f7b3a54219fd3612d7705e7798212935641ed18f" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6d95ae10ce5aa99543a28cf74e41c11f3b9e3c14f0452bbde46024753cd683e" +dependencies = [ + "core-foundation 0.10.1", + "inotify", + "io-kit-sys", + "js-sys", + "libc", + "libudev-sys", + "log", + "nix 0.29.0", + "uuid", + "vec_map", + "wasm-bindgen", + "web-sys", + "windows 0.61.3", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[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 = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" +dependencies = [ + "bytemuck", + "libm", + "rand", + "serde", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gltf" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ce1918195723ce6ac74e80542c5a96a40c2b26162c1957a5cd70799b8cacf7" +dependencies = [ + "byteorder", + "gltf-json", + "lazy_static", + "serde_json", +] + +[[package]] +name = "gltf-derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gltf-json" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14" +dependencies = [ + "gltf-derive", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +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.9.1", + "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.9.1", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows 0.58.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.9.1", + "gpu-descriptor-types", + "hashbrown", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "grid" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36119f3a540b086b4e436bb2b588cf98a68863470e0e880f4d0842f112a3183a" + +[[package]] +name = "guillotiere" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "equivalent", + "foldhash", + "serde", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "portable-atomic", + "stable_deref_trait", +] + +[[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.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hexasphere" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c9e718d32b6e6b2b32354e1b0367025efdd0b11d6a740b905ddf5db1074679" +dependencies = [ + "constgebra", + "glam", + "tinyvec", +] + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", +] + +[[package]] +name = "immutable-chunkmap" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f97096f508d54f8f8ab8957862eee2ccd628847b6217af1a335e1c44dee578" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +dependencies = [ + "console", + "portable-atomic", + "unicode-width 0.2.1", + "unit-prefix", + "web-time", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.9.1", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "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.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "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", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "ktx2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d65e08a9ec02e409d27a0139eaa6b9756b4d81fe7cde71f6941a83730ce838" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder", + "ogg", + "tinyvec", +] + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags 2.9.1", + "libc", + "redox_syscall 0.5.17", +] + +[[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.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "litrs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +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 = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" +dependencies = [ + "libc", +] + +[[package]] +name = "metal" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +dependencies = [ + "bitflags 2.9.1", + "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.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "naga" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" +dependencies = [ + "arrayvec", + "bit-set 0.8.0", + "bitflags 2.9.1", + "cfg_aliases", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "pp-rs", + "rustc-hash 1.1.0", + "spirv", + "strum", + "termcolor", + "thiserror 2.0.12", + "unicode-xid", +] + +[[package]] +name = "naga_oil" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2464f7395decfd16bb4c33fb0cb3b2c645cc60d051bc7fb652d3720bfb20f18" +dependencies = [ + "bit-set 0.5.3", + "codespan-reporting", + "data-encoding", + "indexmap", + "naga", + "once_cell", + "regex", + "regex-syntax 0.8.5", + "rustc-hash 1.1.0", + "thiserror 1.0.69", + "tracing", + "unicode-ident", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.9.1", + "jni-sys", + "log", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.9.1", + "jni-sys", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[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 = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[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 = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.9.1", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d88b1a7538054351c8258338df7c931a590513fb3745e8c15eb9ff4199b8d1" +dependencies = [ + "file-id", + "log", + "notify", + "notify-types", + "walkdir", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[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", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[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-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.9.1", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.1", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2", + "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.9.1", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "oboe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" +dependencies = [ + "jni", + "ndk 0.8.0", + "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 = "offset-allocator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e234d535da3521eb95106f40f0b73483d80bfb3aacf27c40d7e2b72f1a3e00a2" +dependencies = [ + "log", + "nonmax", +] + +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "orbclient" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.17", + "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 = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", + "serde", + "serde_derive", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[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", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.0.8", + "windows-sys 0.60.2", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pp-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radsort" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "019b4b213425016d7d84a153c4c73afb0946fbb4840e4eece7ba8848b9d6da22" + +[[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 0.2.16", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + +[[package]] +name = "rangemap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "read-fonts" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "rectangle-pack" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb" + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rodio" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1" +dependencies = [ + "cpal", + "lewton", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.9.1", + "serde", + "serde_derive", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "self_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "skrifa" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbeb4ca4399663735553a09dd17ce7e49a0a0203f03b706b39628c4d913a8607" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[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.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "smol-hyper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7428a49d323867702cd12b97b08a6b0104f39ec13b49117911f101271321bc1a" +dependencies = [ + "async-executor", + "async-io", + "futures-io", + "hyper", + "pin-project-lite", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "portable-atomic", +] + +[[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.9.1", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stackfuture" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eae92052b72ef70dafa16eddbabffc77e5ca3574be2f7bc1127b36f0a7ad7f2" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[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", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "swash" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f745de914febc7c9ab4388dfaf94bbc87e69f57bb41133a9b0c84d4be49856f3" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "sysinfo" +version = "0.34.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b93974b3d3aeaa036504b8eefd4c039dced109171c1ae973f1dc63b2c7e4b2" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "windows 0.57.0", +] + +[[package]] +name = "taffy" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4f4d046dd956a47a7e1a2947083d7ac3e6aa3cfaaead36173ceaa5ab11878c" +dependencies = [ + "arrayvec", + "grid", + "serde", + "slotmap", +] + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[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.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +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 = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-oslog" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528bdd1f0e27b5dd9a4ededf154e824b0532731e4af73bb531de46276e0aab1e" +dependencies = [ + "bindgen 0.70.1", + "cc", + "cfg-if", + "once_cell", + "parking_lot", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "twox-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unit-prefix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0fde9bc91026e381155f8c67cb354bcd35260b2f4a29bcc84639f762760c39" +dependencies = [ + "base64 0.22.1", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "socks", + "ureq-proto", + "utf-8", + "webpki-roots 0.26.11", +] + +[[package]] +name = "ureq-proto" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7" +dependencies = [ + "base64 0.22.1", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "variadics_please" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "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.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "wgpu" +version = "24.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0b3436f0729f6cdf2e6e9201f3d39dc95813fad61d826c1ed07918b4539353" +dependencies = [ + "arrayvec", + "bitflags 2.9.1", + "cfg_aliases", + "document-features", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "24.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499" +dependencies = [ + "arrayvec", + "bit-vec 0.8.0", + "bitflags 2.9.1", + "cfg_aliases", + "document-features", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.12", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "24.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112f464674ca69f3533248508ee30cb84c67cf06c25ff6800685f5e0294e259" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set 0.8.0", + "bitflags 2.9.1", + "block", + "bytemuck", + "cfg_aliases", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "ordered-float", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.12", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows 0.58.0", + "windows-core 0.58.0", +] + +[[package]] +name = "wgpu-types" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +dependencies = [ + "bitflags 2.9.1", + "js-sys", + "log", + "serde", + "web-sys", +] + +[[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.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" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[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 = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[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_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[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 = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[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_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[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_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[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_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[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_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[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 = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winit" +version = "0.30.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" +dependencies = [ + "android-activity", + "atomic-waker", + "bitflags 2.9.1", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation 0.9.4", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "ndk 0.9.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[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", + "once_cell", + "rustix 0.38.44", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xattr" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" +dependencies = [ + "libc", + "rustix 1.0.8", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.9.1", + "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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a336f80 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "bevy_cef" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[workspace] +resolver = "2" +members = [ + "crates/*", + "examples/demo", +] + +[workspace.package] +version = "0.1.0" +edition = "2024" +license = "Apache-2.0 OR MIT" +authors = ["notelm"] +repository = "https://github.com/not-elm/bevy_cef" + +[workspace.dependencies] +bevy = { version = "0.16" } +bevy_remote = "0.16" +cef = { version = "138" } +cef-dll-sys = { version = "138", features = ["sandbox"] } +download-cef = { version = "2" } +bevy_cef = { path = "." } +bevy_cef_core = { path = "crates/bevy_cef_core" } +async-channel = { version = "2.5" } +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1" } +raw-window-handle = "0.6" + +[dependencies] +bevy = { workspace = true } +bevy_remote = { workspace = true } +cef = { workspace = true } +bevy_cef_core = { workspace = true } +async-channel = { version = "2.5" } +serde = { workspace = true } +serde_json = { workspace = true } +raw-window-handle = "0.6" + +[dev-dependencies] +bevy = { workspace = true, features = ["file_watcher"]} +bevy_cef = { workspace = true, features = ["debug"] } + +[target.'cfg(target_os = "macos")'.dependencies] +objc = { version = "0.2" } + +[features] +default = [] +serialize = ["bevy/serialize"] +debug = ["bevy_cef_core/debug"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2964840 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +fix: + cargo clippy --fix --allow-dirty --allow-staged --workspace --all --all-features + cargo fmt --all + +install: + cargo install --path ./crates/bevy_cef_debug_render_process --force \ No newline at end of file diff --git a/assets/brp.html b/assets/brp.html new file mode 100644 index 0000000..e6ffa7c --- /dev/null +++ b/assets/brp.html @@ -0,0 +1,35 @@ + + + + + Title + + +

HTML Path: assets/brp.html

+

+ + +

+ + + \ No newline at end of file diff --git a/assets/host_emit.html b/assets/host_emit.html new file mode 100644 index 0000000..3607ecd --- /dev/null +++ b/assets/host_emit.html @@ -0,0 +1,23 @@ + + + + + Host Emit + + +

HTML Path: assets/host_emit.html

+

+ This example demonstrates how to receive events from the application. +

+

+ The application emits a count value every second, which is then displayed in the HTML. +

+

0

+ + + \ No newline at end of file diff --git a/assets/images/rustacean-flat-gesture.png b/assets/images/rustacean-flat-gesture.png new file mode 100644 index 0000000..866412d Binary files /dev/null and b/assets/images/rustacean-flat-gesture.png differ diff --git a/assets/js_emit.html b/assets/js_emit.html new file mode 100644 index 0000000..081125f --- /dev/null +++ b/assets/js_emit.html @@ -0,0 +1,28 @@ + + + + + JS Emit + + +

HTML Path: assets/js_emit.html

+ +

The example sends events from JavaScript to the application.

+

+ It emits a count value every second, which is then sent to the application and logged in the console. +

+

0

+ + + \ No newline at end of file diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl new file mode 100644 index 0000000..4488d28 --- /dev/null +++ b/assets/shaders/custom_material.wgsl @@ -0,0 +1,20 @@ +#import bevy_pbr::{ + forward_io::VertexOutput, +} +#import webview::util::{ + surface_color, +} + +@group(2) @binding(0) var mask_texture: texture_2d; +@group(2) @binding(1) var mask_sampler: sampler; + +@fragment +fn fragment( + in: VertexOutput, +) -> @location(0) vec4 { + // You can obtain the surface color. + var color = surface_color(in.uv); + // Blend the color with the mask texture. + color *= (textureSample(mask_texture, mask_sampler, in.uv) * vec4(vec3(1.0), 0.3)); + return color; +} diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6829e30 --- /dev/null +++ b/build.rs @@ -0,0 +1,59 @@ +use std::env::home_dir; +use std::process::Command; + +fn main() -> std::io::Result<()> { + println!("cargo::rerun-if-changed=build.rs"); + #[cfg(all(target_os = "macos", feature = "debug"))] + { + install_bevy_cef_debug_render_process()?; + install_export_cef_dir()?; + export_cef_dir()?; + } + Ok(()) +} + +fn install_bevy_cef_debug_render_process() -> std::io::Result<()> { + let bevy_cef_render_process_path = home_dir() + .unwrap() + .join(".cargo") + .join("bin") + .join("bevy_cef_debug_render_process"); + if !bevy_cef_render_process_path.exists() { + Command::new("cargo") + .args(["install", "bevy_cef_debug_render_process"]) + .spawn()?; + } + Ok(()) +} + +fn install_export_cef_dir() -> std::io::Result<()> { + let export_cef_dir_path = home_dir() + .unwrap() + .join(".cargo") + .join("bin") + .join("export-cef-dir"); + if !export_cef_dir_path.exists() { + Command::new("cargo") + .args(["install", "export-cef-dir"]) + .spawn()?; + } + Ok(()) +} + +fn export_cef_dir() -> std::io::Result<()> { + let cef_dir = home_dir().unwrap().join(".local").join("share").join("cef"); + if cef_dir.exists() { + return Ok(()); + } + let export_cef_dir_path = home_dir() + .unwrap() + .join(".cargo") + .join("bin") + .join("export-cef-dir"); + Command::new(export_cef_dir_path) + .arg("--force") + .arg(cef_dir) + .spawn()? + .wait()?; + Ok(()) +} diff --git a/crates/bevy_cef_core/Cargo.toml b/crates/bevy_cef_core/Cargo.toml new file mode 100644 index 0000000..3d67b7a --- /dev/null +++ b/crates/bevy_cef_core/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bevy_cef_core" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +bevy = { workspace = true } +bevy_remote = { workspace = true } +uuid = { version = "1" } +cef = { workspace = true } +cef-dll-sys = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +async-channel = { workspace = true } +raw-window-handle = { workspace = true } + +[features] +default = [] +debug = [] \ No newline at end of file diff --git a/crates/bevy_cef_core/src/browser_process.rs b/crates/bevy_cef_core/src/browser_process.rs new file mode 100644 index 0000000..0628f38 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process.rs @@ -0,0 +1,18 @@ +mod app; +mod browser_process_handler; +mod browsers; +mod client_handler; +mod context_menu_handler; +mod display_handler; +mod localhost; +mod renderer_handler; +mod request_context_handler; + +pub use app::*; +pub use browser_process_handler::*; +pub use browsers::*; +pub use client_handler::*; +pub use context_menu_handler::*; +pub use localhost::*; +pub use renderer_handler::*; +pub use request_context_handler::*; diff --git a/crates/bevy_cef_core/src/browser_process/app.rs b/crates/bevy_cef_core/src/browser_process/app.rs new file mode 100644 index 0000000..21d02bc --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/app.rs @@ -0,0 +1,85 @@ +use crate::browser_process::browser_process_handler::BrowserProcessHandlerBuilder; +use crate::util::{SCHEME_CEF, cef_scheme_flags}; +use cef::rc::{Rc, RcImpl}; +use cef::{ + BrowserProcessHandler, CefString, CommandLine, ImplApp, ImplCommandLine, ImplSchemeRegistrar, + SchemeRegistrar, WrapApp, +}; +use cef_dll_sys::{_cef_app_t, cef_base_ref_counted_t}; + +/// ## Reference +/// +/// - [`CefApp Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefApp.html) +#[derive(Default)] +pub struct BrowserProcessAppBuilder { + object: *mut RcImpl<_cef_app_t, Self>, +} + +impl BrowserProcessAppBuilder { + pub fn build() -> cef::App { + cef::App::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl Clone for BrowserProcessAppBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + self.object + }; + Self { object } + } +} + +impl Rc for BrowserProcessAppBuilder { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl ImplApp for BrowserProcessAppBuilder { + fn on_before_command_line_processing( + &self, + _: Option<&CefString>, + command_line: Option<&mut CommandLine>, + ) { + let Some(command_line) = command_line else { + return; + }; + //TODO: フラグで切り替えるようにする + command_line.append_switch(Some(&"use-mock-keychain".into())); + #[cfg(feature = "debug")] + { + command_line.append_switch(Some(&"disable-gpu".into())); + command_line.append_switch(Some(&"disable-gpu-compositing".into())); + command_line.append_switch(Some(&"disable-software-rasterizer".into())); + } + } + + fn browser_process_handler(&self) -> Option { + Some(BrowserProcessHandlerBuilder::build()) + } + + fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) { + if let Some(registrar) = registrar { + registrar.add_custom_scheme(Some(&SCHEME_CEF.into()), cef_scheme_flags() as _); + } + } + + #[inline] + fn get_raw(&self) -> *mut _cef_app_t { + self.object as *mut cef::sys::_cef_app_t + } +} + +impl WrapApp for BrowserProcessAppBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) { + self.object = object; + } +} diff --git a/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs b/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs new file mode 100644 index 0000000..c48598f --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs @@ -0,0 +1,63 @@ +use cef::rc::{Rc, RcImpl}; +use cef::*; + +/// ## Reference +/// +/// - [`CefBrowserProcessHandler Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefBrowserProcessHandler.html) +pub struct BrowserProcessHandlerBuilder { + object: *mut RcImpl, +} + +impl BrowserProcessHandlerBuilder { + pub fn build() -> BrowserProcessHandler { + BrowserProcessHandler::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl Rc for BrowserProcessHandlerBuilder { + fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapBrowserProcessHandler for BrowserProcessHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for BrowserProcessHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + + Self { object } + } +} + +impl ImplBrowserProcessHandler for BrowserProcessHandlerBuilder { + fn on_before_child_process_launch(&self, command_line: Option<&mut CommandLine>) { + let Some(command_line) = command_line else { + return; + }; + + command_line.append_switch(Some(&"disable-web-security".into())); + command_line.append_switch(Some(&"allow-running-insecure-content".into())); + command_line.append_switch(Some(&"disable-session-crashed-bubble".into())); + command_line.append_switch(Some(&"ignore-certificate-errors".into())); + command_line.append_switch(Some(&"ignore-ssl-errors".into())); + command_line.append_switch(Some(&"enable-logging=stderr".into())); + } + #[inline] + fn get_raw(&self) -> *mut cef_dll_sys::_cef_browser_process_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/browser_process/browsers.rs b/crates/bevy_cef_core/src/browser_process/browsers.rs new file mode 100644 index 0000000..7569392 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/browsers.rs @@ -0,0 +1,492 @@ +use crate::browser_process::BrpHandler; +use crate::browser_process::ClientHandlerBuilder; +use crate::browser_process::client_handler::{IpcEventRaw, JsEmitEventHandler}; +use crate::prelude::IntoString; +use crate::prelude::*; +use async_channel::{Sender, TryRecvError}; +use bevy::platform::collections::HashMap; +use bevy::prelude::*; +use bevy_remote::BrpMessage; +use cef::{ + Browser, BrowserHost, BrowserSettings, Client, CompositionUnderline, ImplBrowser, + ImplBrowserHost, ImplFrame, ImplListValue, ImplProcessMessage, ImplRequestContext, + MouseButtonType, ProcessId, Range, RequestContext, RequestContextSettings, WindowInfo, + browser_host_create_browser_sync, process_message_create, +}; +use cef_dll_sys::{cef_event_flags_t, cef_mouse_button_type_t}; +#[allow(deprecated)] +use raw_window_handle::RawWindowHandle; +use std::cell::Cell; +use std::rc::Rc; + +mod devtool_render_handler; +mod keyboard; + +use crate::browser_process::browsers::devtool_render_handler::DevToolRenderHandlerBuilder; +use crate::browser_process::display_handler::{DisplayHandlerBuilder, SystemCursorIconSenderInner}; +pub use keyboard::*; + +pub struct WebviewBrowser { + pub client: Browser, + pub host: BrowserHost, + pub size: SharedViewSize, +} + +pub struct Browsers { + browsers: HashMap, + sender: TextureSender, + receiver: TextureReceiver, + ime_caret: SharedImeCaret, +} + +impl Default for Browsers { + fn default() -> Self { + let (sender, receiver) = async_channel::unbounded::(); + Browsers { + browsers: HashMap::default(), + sender, + receiver, + ime_caret: Rc::new(Cell::new(0)), + } + } +} + +impl Browsers { + #[allow(clippy::too_many_arguments)] + pub fn create_browser( + &mut self, + webview: Entity, + uri: &str, + webview_size: Vec2, + requester: Requester, + ipc_event_sender: Sender, + brp_sender: Sender, + system_cursor_icon_sender: SystemCursorIconSenderInner, + window_handle: Option, + ) { + let mut context = Self::request_context(requester); + let size = Rc::new(Cell::new(webview_size)); + let browser = browser_host_create_browser_sync( + Some(&WindowInfo { + windowless_rendering_enabled: true as _, + external_begin_frame_enabled: true as _, + parent_view: match window_handle { + Some(RawWindowHandle::AppKit(handle)) => handle.ns_view.as_ptr(), + Some(RawWindowHandle::Win32(handle)) => handle.hwnd.get() as _, + Some(RawWindowHandle::Xlib(handle)) => handle.window as _, + Some(RawWindowHandle::Wayland(handle)) => handle.surface.as_ptr(), + _ => std::ptr::null_mut(), + }, + // shared_texture_enabled: true as _, + ..Default::default() + }), + Some(&mut self.client_handler( + webview, + size.clone(), + ipc_event_sender, + brp_sender, + system_cursor_icon_sender, + )), + Some(&uri.into()), + Some(&BrowserSettings { + windowless_frame_rate: 60, + ..Default::default() + }), + None, + context.as_mut(), + ) + .expect("Failed to create browser"); + self.browsers.insert( + webview, + WebviewBrowser { + host: browser.host().expect("Failed to get browser host"), + client: browser, + size, + }, + ); + } + + pub fn send_external_begin_frame(&mut self) { + for browser in self.browsers.values_mut() { + browser.host.send_external_begin_frame(); + } + } + + pub fn send_mouse_move<'a>( + &self, + webview: &Entity, + buttons: impl IntoIterator, + position: Vec2, + mouse_leave: bool, + ) { + if let Some(browser) = self.get_focused_browser(webview) { + let mouse_event = cef::MouseEvent { + x: position.x as i32, + y: position.y as i32, + modifiers: modifiers_from_mouse_buttons(buttons), + }; + browser + .host + .send_mouse_move_event(Some(&mouse_event), mouse_leave as _); + } + } + + pub fn send_mouse_click( + &self, + webview: &Entity, + position: Vec2, + button: PointerButton, + mouse_up: bool, + ) { + if let Some(browser) = self.get_focused_browser(webview) { + let mouse_event = cef::MouseEvent { + x: position.x as i32, + y: position.y as i32, + modifiers: match button { + PointerButton::Primary => cef_event_flags_t::EVENTFLAG_LEFT_MOUSE_BUTTON, + PointerButton::Secondary => cef_event_flags_t::EVENTFLAG_RIGHT_MOUSE_BUTTON, + PointerButton::Middle => cef_event_flags_t::EVENTFLAG_MIDDLE_MOUSE_BUTTON, + } as _, // No modifiers for simplicity + }; + let mouse_button = match button { + PointerButton::Secondary => cef_mouse_button_type_t::MBT_RIGHT, + PointerButton::Middle => cef_mouse_button_type_t::MBT_MIDDLE, + _ => cef_mouse_button_type_t::MBT_LEFT, + }; + browser.host.set_focus(true as _); + browser.host.send_mouse_click_event( + Some(&mouse_event), + MouseButtonType::from(mouse_button), + mouse_up as _, + 1, + ); + } + } + + /// [`SendMouseWheelEvent`](https://cef-builds.spotifycdn.com/docs/106.1/classCefBrowserHost.html#acd5d057bd5230baa9a94b7853ba755f7) + pub fn send_mouse_wheel(&self, webview: &Entity, position: Vec2, delta: Vec2) { + if let Some(browser) = self.get_focused_browser(webview) { + let mouse_event = cef::MouseEvent { + x: position.x as i32, + y: position.y as i32, + modifiers: 0, + }; + browser + .host + .send_mouse_wheel_event(Some(&mouse_event), delta.x as _, delta.y as _); + } + } + + #[inline] + pub fn send_key(&self, webview: &Entity, event: cef::KeyEvent) { + if let Some(browser) = self.get_focused_browser(webview) { + browser.host.send_key_event(Some(&event)); + } + } + + pub fn emit_event(&self, webview: &Entity, id: impl Into, event: &serde_json::Value) { + if let Some(mut process_message) = + process_message_create(Some(&PROCESS_MESSAGE_HOST_EMIT.into())) + && let Some(argument_list) = process_message.argument_list() + && let Some(browser) = self.browsers.get(webview) + && let Some(frame) = browser.client.main_frame() + { + argument_list.set_string(0, Some(&id.into().as_str().into())); + argument_list.set_string(1, Some(&event.to_string().as_str().into())); + frame.send_process_message( + ProcessId::from(cef_dll_sys::cef_process_id_t::PID_RENDERER), + Some(&mut process_message), + ); + }; + } + + pub fn resize(&self, webview: &Entity, size: Vec2) { + if let Some(browser) = self.browsers.get(webview) { + browser.size.set(size); + browser.host.was_resized(); + } + } + + /// Closes the browser associated with the given webview entity. + /// + /// The browser will be removed from the hash map after closing. + pub fn close(&mut self, webview: &Entity) { + if let Some(browser) = self.browsers.remove(webview) { + browser.host.close_browser(true as _); + debug!("Closed browser with webview: {:?}", webview); + } + } + + #[inline] + pub fn try_receive_texture(&self) -> core::result::Result { + self.receiver.try_recv() + } + + /// Shows the DevTools for the specified webview. + pub fn show_devtool(&self, webview: &Entity) { + let Some(browser) = self.browsers.get(webview) else { + return; + }; + browser.host.show_dev_tools( + Some(&WindowInfo::default()), + Some(&mut ClientHandlerBuilder::new(DevToolRenderHandlerBuilder::build()).build()), + Some(&BrowserSettings::default()), + None, + ); + } + + /// Closes the DevTools for the specified webview. + pub fn close_devtools(&self, webview: &Entity) { + if let Some(browser) = self.browsers.get(webview) { + browser.host.close_dev_tools(); + } + } + + /// Navigate backwards. + /// + /// ## Reference + /// + /// - [`GoBack`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowser.html#a85b02760885c070e4ad2a2705cea56cb) + pub fn go_back(&self, webview: &Entity) { + if let Some(browser) = self.browsers.get(webview) + && browser.client.can_go_back() == 1 + { + browser.client.go_back(); + } + } + + /// Navigate forwards. + /// + /// ## Reference + /// + /// - [`GoForward`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowser.html#aa8e97fc210ee0e73f16b2d98482419d0) + pub fn go_forward(&self, webview: &Entity) { + if let Some(browser) = self.browsers.get(webview) + && browser.client.can_go_forward() == 1 + { + browser.client.go_forward(); + } + } + + /// Returns the current zoom level for the specified webview. + /// + /// ## Reference + /// + /// - [`GetZoomLevel`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#a524d4a358287dab284c0dfec6d6d229e) + pub fn zoom_level(&self, webview: &Entity) -> Option { + self.browsers + .get(webview) + .map(|browser| browser.host.zoom_level()) + } + + /// Sets the zoom level for the specified webview. + /// + /// ## Reference + /// + /// - [`SetZoomLevel`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#af2b7bf250ac78345117cd575190f2f7b) + pub fn set_zoom_level(&self, webview: &Entity, zoom_level: f64) { + if let Some(browser) = self.browsers.get(webview) { + browser.host.set_zoom_level(zoom_level); + } + } + + /// Sets whether the audio is muted for the specified webview. + /// + /// ## Reference + /// + /// - [`SetAudioMuted`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#a153d179c9ff202c8bb8869d2e9a820a2) + pub fn set_audio_muted(&self, webview: &Entity, muted: bool) { + if let Some(browser) = self.browsers.get(webview) { + browser.host.set_audio_muted(muted as _); + } + } + + #[inline] + pub fn reload(&self) { + for browser in self.browsers.values() { + if let Some(frame) = browser.client.main_frame() { + let url = frame.url().into_string(); + info!("Reloading browser with URL: {}", url); + frame.load_url(Some(&url.as_str().into())); + } + } + } + + /// ## Reference + /// + /// - [`ImeSetComposition`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#a567b41fb2d3917843ece3b57adc21ebe) + pub fn set_ime_composition(&self, text: &str, cursor_utf16: Option) { + let underlines = make_underlines_for(text, cursor_utf16.map(|i| (i, i))); + let i = text.encode_utf16().count(); + let selection_range = Range { + from: i as _, + to: i as _, + }; + let replacement_range = self.ime_caret_range(); + for browser in self + .browsers + .values() + .filter(|b| b.client.focused_frame().is_some()) + { + browser.host.ime_set_composition( + Some(&text.into()), + underlines.len(), + Some(&underlines[0]), + Some(&replacement_range), + Some(&selection_range), + ); + } + } + + /// ## Reference + /// + /// [`ImeSetComposition`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#a567b41fb2d3917843ece3b57adc21ebe) + pub fn ime_finish_composition(&self, keep_selection: bool) { + for browser in self + .browsers + .values() + .filter(|b| b.client.focused_frame().is_some()) + { + browser.host.ime_finish_composing_text(keep_selection as _); + } + } + + pub fn set_ime_commit_text(&self, text: &str) { + let replacement_range = self.ime_caret_range(); + for browser in self + .browsers + .values() + .filter(|b| b.client.focused_frame().is_some()) + { + browser + .host + .ime_commit_text(Some(&text.into()), Some(&replacement_range), 0) + } + } + + fn request_context(requester: Requester) -> Option { + let mut context = cef::request_context_create_context( + Some(&RequestContextSettings::default()), + Some(&mut RequestContextHandlerBuilder::build()), + ); + if let Some(context) = context.as_mut() { + context.register_scheme_handler_factory( + Some(&SCHEME_CEF.into()), + Some(&HOST_CEF.into()), + Some(&mut LocalSchemaHandlerBuilder::build(requester)), + ); + } + context + } + + fn client_handler( + &self, + webview: Entity, + size: SharedViewSize, + ipc_event_sender: Sender, + brp_sender: Sender, + system_cursor_icon_sender: SystemCursorIconSenderInner, + ) -> Client { + ClientHandlerBuilder::new(RenderHandlerBuilder::build( + webview, + self.sender.clone(), + size.clone(), + self.ime_caret.clone(), + )) + .with_display_handler(DisplayHandlerBuilder::build(system_cursor_icon_sender)) + .with_message_handler(JsEmitEventHandler::new(webview, ipc_event_sender)) + .with_message_handler(BrpHandler::new(brp_sender)) + .build() + } + + #[inline] + fn ime_caret_range(&self) -> Range { + let caret = self.ime_caret.get(); + Range { + from: caret, + to: caret, + } + } + + #[inline] + fn get_focused_browser(&self, webview: &Entity) -> Option<&WebviewBrowser> { + self.browsers + .get(webview) + .and_then(|b| b.client.focused_frame().is_some().then_some(b)) + } +} + +pub fn modifiers_from_mouse_buttons<'a>(buttons: impl IntoIterator) -> u32 { + let mut modifiers = cef_event_flags_t::EVENTFLAG_NONE as u32; + for button in buttons { + match button { + MouseButton::Left => modifiers |= cef_event_flags_t::EVENTFLAG_LEFT_MOUSE_BUTTON as u32, + MouseButton::Right => { + modifiers |= cef_event_flags_t::EVENTFLAG_RIGHT_MOUSE_BUTTON as u32 + } + MouseButton::Middle => { + modifiers |= cef_event_flags_t::EVENTFLAG_MIDDLE_MOUSE_BUTTON as u32 + } + _ => {} + } + } + modifiers +} + +pub fn make_underlines_for( + text: &str, + selection_utf16: Option<(u32, u32)>, +) -> Vec { + let len16 = utf16_len(text); + + let base = CompositionUnderline { + size: size_of::(), + range: Range { from: 0, to: len16 }, + color: 0, + background_color: 0, + thick: 0, + style: Default::default(), + }; + + if let Some((from, to)) = selection_utf16 + && from < to + { + let sel = CompositionUnderline { + size: size_of::(), + range: Range { from, to }, + color: 0, + background_color: 0, + thick: 1, + style: Default::default(), + }; + return vec![base, sel]; + } + vec![base] +} + +#[inline] +fn utf16_len(s: &str) -> u32 { + s.encode_utf16().count() as u32 +} + +#[allow(dead_code)] +fn utf16_index_from_byte(s: &str, byte_idx: usize) -> u32 { + s[..byte_idx].encode_utf16().count() as u32 +} + +#[cfg(test)] +mod tests { + use crate::prelude::modifiers_from_mouse_buttons; + use bevy::prelude::*; + + #[test] + fn test_modifiers_from_mouse_buttons() { + let buttons = vec![&MouseButton::Left, &MouseButton::Right]; + let modifiers = modifiers_from_mouse_buttons(buttons); + assert_eq!( + modifiers, + cef_dll_sys::cef_event_flags_t::EVENTFLAG_LEFT_MOUSE_BUTTON as u32 + | cef_dll_sys::cef_event_flags_t::EVENTFLAG_RIGHT_MOUSE_BUTTON as u32 + ); + } +} diff --git a/crates/bevy_cef_core/src/browser_process/browsers/devtool_render_handler.rs b/crates/bevy_cef_core/src/browser_process/browsers/devtool_render_handler.rs new file mode 100644 index 0000000..054e325 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/browsers/devtool_render_handler.rs @@ -0,0 +1,57 @@ +use cef::rc::{Rc, RcImpl}; +use cef::{Browser, ImplRenderHandler, Rect, RenderHandler, WrapRenderHandler, sys}; + +/// ## Reference +/// +/// - [`CefRenderHandler Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefRenderHandler.html) +pub struct DevToolRenderHandlerBuilder { + object: *mut RcImpl, +} + +impl DevToolRenderHandlerBuilder { + pub fn build() -> RenderHandler { + RenderHandler::new(Self { + object: std::ptr::null_mut(), + }) + } +} + +impl Rc for DevToolRenderHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapRenderHandler for DevToolRenderHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for DevToolRenderHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { object } + } +} + +impl ImplRenderHandler for DevToolRenderHandlerBuilder { + fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) { + if let Some(rect) = rect { + rect.width = 800; + rect.height = 800; + } + } + + #[inline] + fn get_raw(&self) -> *mut sys::_cef_render_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/browser_process/browsers/keyboard.rs b/crates/bevy_cef_core/src/browser_process/browsers/keyboard.rs new file mode 100644 index 0000000..90c1fe1 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/browsers/keyboard.rs @@ -0,0 +1,979 @@ +//! ## Reference +//! +//! - [`cef_key_event_t`](https://cef-builds.spotifycdn.com/docs/106.1/structcef__key__event__t.html) +//! - [KeyboardCodes](https://chromium.googlesource.com/external/Webkit/+/safari-4-branch/WebCore/platform/KeyboardCodes.h) + +use bevy::input::ButtonState; +use bevy::input::keyboard::KeyboardInput; +use bevy::prelude::{ButtonInput, KeyCode}; +use cef_dll_sys::{cef_event_flags_t, cef_key_event_t, cef_key_event_type_t}; + +pub fn keyboard_modifiers(input: &ButtonInput) -> u32 { + let mut flags = 0u32; + + if input.pressed(KeyCode::ControlLeft) || input.pressed(KeyCode::ControlRight) { + flags |= cef_event_flags_t::EVENTFLAG_CONTROL_DOWN as u32; + } + if input.pressed(KeyCode::AltLeft) || input.pressed(KeyCode::AltRight) { + flags |= cef_event_flags_t::EVENTFLAG_ALT_DOWN as u32; + } + if input.pressed(KeyCode::ShiftLeft) || input.pressed(KeyCode::ShiftRight) { + flags |= cef_event_flags_t::EVENTFLAG_SHIFT_DOWN as u32; + } + if input.pressed(KeyCode::SuperLeft) || input.pressed(KeyCode::SuperRight) { + flags |= cef_event_flags_t::EVENTFLAG_COMMAND_DOWN as u32; + } + if input.pressed(KeyCode::CapsLock) { + flags |= cef_event_flags_t::EVENTFLAG_CAPS_LOCK_ON as u32; + } + if input.pressed(KeyCode::NumLock) { + flags |= cef_event_flags_t::EVENTFLAG_NUM_LOCK_ON as u32; + } + + flags +} + +pub fn create_cef_key_event( + modifiers: u32, + _input: &ButtonInput, + key_event: &KeyboardInput, +) -> Option { + let key_type = match key_event.state { + // ButtonState::Pressed if input.just_pressed(key_event.key_code) => { + // cef_key_event_type_t::KEYEVENT_RAWKEYDOWN + // } + ButtonState::Pressed => cef_key_event_type_t::KEYEVENT_CHAR, + ButtonState::Released => cef_key_event_type_t::KEYEVENT_KEYUP, + }; + let windows_key_code = keycode_to_windows_vk(key_event.key_code); + + let character = key_event + .text + .as_ref() + .and_then(|text| text.chars().next()) + .unwrap_or('\0') as u16; + + Some(cef::KeyEvent::from(cef_key_event_t { + size: core::mem::size_of::(), + type_: key_type, + modifiers, + windows_key_code, + native_key_code: to_native_key_code(&key_event.key_code) as _, + character, + unmodified_character: character, + is_system_key: false as _, + focus_on_editable_field: false as _, + })) +} + +// fn is_not_character_key_code(keycode: &KeyCode) -> bool { +// match keycode { +// // Function keys are not character keys +// KeyCode::F1 +// | KeyCode::F2 +// | KeyCode::F3 +// | KeyCode::F4 +// | KeyCode::F5 +// | KeyCode::F6 +// | KeyCode::F7 +// | KeyCode::F8 +// | KeyCode::F9 +// | KeyCode::F10 +// | KeyCode::F11 +// | KeyCode::F12 => true, +// +// // Navigation keys are not character keys +// KeyCode::ArrowLeft +// | KeyCode::ArrowUp +// | KeyCode::ArrowRight +// | KeyCode::ArrowDown +// | KeyCode::Home +// | KeyCode::End +// | KeyCode::PageUp +// | KeyCode::PageDown => true, +// +// // Modifier keys are not character keys +// KeyCode::ShiftLeft +// | KeyCode::ShiftRight +// | KeyCode::ControlLeft +// | KeyCode::ControlRight +// | KeyCode::AltLeft +// | KeyCode::AltRight +// | KeyCode::SuperLeft +// | KeyCode::SuperRight => true, +// +// // Lock keys are not character keys +// KeyCode::CapsLock | KeyCode::NumLock | KeyCode::ScrollLock => true, +// +// // Special control keys are not character keys +// KeyCode::Escape +// | KeyCode::Tab +// | KeyCode::Enter +// | KeyCode::Backspace +// | KeyCode::Delete +// | KeyCode::Insert => true, +// +// // All other keys (letters, numbers, punctuation, space, numpad) are character keys +// _ => false, +// } +// } + +fn keycode_to_windows_vk(keycode: KeyCode) -> i32 { + match keycode { + // Letters + KeyCode::KeyA => 0x41, + KeyCode::KeyB => 0x42, + KeyCode::KeyC => 0x43, + KeyCode::KeyD => 0x44, + KeyCode::KeyE => 0x45, + KeyCode::KeyF => 0x46, + KeyCode::KeyG => 0x47, + KeyCode::KeyH => 0x48, + KeyCode::KeyI => 0x49, + KeyCode::KeyJ => 0x4A, + KeyCode::KeyK => 0x4B, + KeyCode::KeyL => 0x4C, + KeyCode::KeyM => 0x4D, + KeyCode::KeyN => 0x4E, + KeyCode::KeyO => 0x4F, + KeyCode::KeyP => 0x50, + KeyCode::KeyQ => 0x51, + KeyCode::KeyR => 0x52, + KeyCode::KeyS => 0x53, + KeyCode::KeyT => 0x54, + KeyCode::KeyU => 0x55, + KeyCode::KeyV => 0x56, + KeyCode::KeyW => 0x57, + KeyCode::KeyX => 0x58, + KeyCode::KeyY => 0x59, + KeyCode::KeyZ => 0x5A, + + // Numbers + KeyCode::Digit0 => 0x30, + KeyCode::Digit1 => 0x31, + KeyCode::Digit2 => 0x32, + KeyCode::Digit3 => 0x33, + KeyCode::Digit4 => 0x34, + KeyCode::Digit5 => 0x35, + KeyCode::Digit6 => 0x36, + KeyCode::Digit7 => 0x37, + KeyCode::Digit8 => 0x38, + KeyCode::Digit9 => 0x39, + + // Function keys + KeyCode::F1 => 0x70, + KeyCode::F2 => 0x71, + KeyCode::F3 => 0x72, + KeyCode::F4 => 0x73, + KeyCode::F5 => 0x74, + KeyCode::F6 => 0x75, + KeyCode::F7 => 0x76, + KeyCode::F8 => 0x77, + KeyCode::F9 => 0x78, + KeyCode::F10 => 0x79, + KeyCode::F11 => 0x7A, + KeyCode::F12 => 0x7B, + + // Special keys + KeyCode::Enter => 0x0D, + KeyCode::Space => 0x20, + KeyCode::Backspace => 0x08, + KeyCode::Delete => 0x2E, + KeyCode::Tab => 0x09, + KeyCode::Escape => 0x1B, + KeyCode::Insert => 0x2D, + KeyCode::Home => 0x24, + KeyCode::End => 0x23, + KeyCode::PageUp => 0x21, + KeyCode::PageDown => 0x22, + + // Arrow keys + KeyCode::ArrowLeft => 0x25, + KeyCode::ArrowUp => 0x26, + KeyCode::ArrowRight => 0x27, + KeyCode::ArrowDown => 0x28, + + // Modifier keys + KeyCode::ShiftLeft | KeyCode::ShiftRight => 0x10, + KeyCode::ControlLeft | KeyCode::ControlRight => 0x11, + KeyCode::AltLeft | KeyCode::AltRight => 0x12, + KeyCode::SuperLeft => 0x5B, // Left Windows key + KeyCode::SuperRight => 0x5C, // Right Windows key + + // Lock keys + KeyCode::CapsLock => 0x14, + KeyCode::NumLock => 0x90, + KeyCode::ScrollLock => 0x91, + + // Punctuation + KeyCode::Semicolon => 0xBA, + KeyCode::Equal => 0xBB, + KeyCode::Comma => 0xBC, + KeyCode::Minus => 0xBD, + KeyCode::Period => 0xBE, + KeyCode::Slash => 0xBF, + KeyCode::Backquote => 0xC0, + KeyCode::BracketLeft => 0xDB, + KeyCode::Backslash => 0xDC, + KeyCode::BracketRight => 0xDD, + KeyCode::Quote => 0xDE, + + // Numpad + KeyCode::Numpad0 => 0x60, + KeyCode::Numpad1 => 0x61, + KeyCode::Numpad2 => 0x62, + KeyCode::Numpad3 => 0x63, + KeyCode::Numpad4 => 0x64, + KeyCode::Numpad5 => 0x65, + KeyCode::Numpad6 => 0x66, + KeyCode::Numpad7 => 0x67, + KeyCode::Numpad8 => 0x68, + KeyCode::Numpad9 => 0x69, + KeyCode::NumpadMultiply => 0x6A, + KeyCode::NumpadAdd => 0x6B, + KeyCode::NumpadSubtract => 0x6D, + KeyCode::NumpadDecimal => 0x6E, + KeyCode::NumpadDivide => 0x6F, + + // Default case for unhandled keys + _ => 0, + } +} + +// fn is_special_key(keycode: &KeyCode) -> bool { +// matches!( +// keycode, +// KeyCode::Enter +// | KeyCode::Space +// | KeyCode::Backspace +// | KeyCode::Delete +// | KeyCode::Tab +// | KeyCode::Escape +// | KeyCode::Insert +// | KeyCode::Home +// | KeyCode::End +// | KeyCode::PageUp +// | KeyCode::PageDown +// ) +// } + +/// Native key codes for different platforms based on MDN documentation +/// [`Keyboard_event_key_values`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) +fn to_native_key_code(keycode: &KeyCode) -> u32 { + match keycode { + // Letters - Platform specific native codes + KeyCode::KeyA => { + if cfg!(target_os = "macos") { + 0x00 + } else { + 0x41 + } // Linux/default + } + KeyCode::KeyB => { + if cfg!(target_os = "macos") { + 0x0B + } else { + 0x42 + } + } + KeyCode::KeyC => { + if cfg!(target_os = "macos") { + 0x08 + } else { + 0x43 + } + } + KeyCode::KeyD => { + if cfg!(target_os = "macos") { + 0x02 + } else { + 0x44 + } + } + KeyCode::KeyE => { + if cfg!(target_os = "macos") { + 0x0E + } else { + 0x45 + } + } + KeyCode::KeyF => { + if cfg!(target_os = "macos") { + 0x03 + } else { + 0x46 + } + } + KeyCode::KeyG => { + if cfg!(target_os = "macos") { + 0x05 + } else { + 0x47 + } + } + KeyCode::KeyH => { + if cfg!(target_os = "macos") { + 0x04 + } else { + 0x48 + } + } + KeyCode::KeyI => { + if cfg!(target_os = "macos") { + 0x22 + } else { + 0x49 + } + } + KeyCode::KeyJ => { + if cfg!(target_os = "macos") { + 0x26 + } else { + 0x4A + } + } + KeyCode::KeyK => { + if cfg!(target_os = "macos") { + 0x28 + } else { + 0x4B + } + } + KeyCode::KeyL => { + if cfg!(target_os = "macos") { + 0x25 + } else { + 0x4C + } + } + KeyCode::KeyM => { + if cfg!(target_os = "macos") { + 0x2E + } else { + 0x4D + } + } + KeyCode::KeyN => { + if cfg!(target_os = "macos") { + 0x2D + } else { + 0x4E + } + } + KeyCode::KeyO => { + if cfg!(target_os = "macos") { + 0x1F + } else { + 0x4F + } + } + KeyCode::KeyP => { + if cfg!(target_os = "macos") { + 0x23 + } else { + 0x50 + } + } + KeyCode::KeyQ => { + if cfg!(target_os = "macos") { + 0x0C + } else { + 0x51 + } + } + KeyCode::KeyR => { + if cfg!(target_os = "macos") { + 0x0F + } else { + 0x52 + } + } + KeyCode::KeyS => { + if cfg!(target_os = "macos") { + 0x01 + } else { + 0x53 + } + } + KeyCode::KeyT => { + if cfg!(target_os = "macos") { + 0x11 + } else { + 0x54 + } + } + KeyCode::KeyU => { + if cfg!(target_os = "macos") { + 0x20 + } else { + 0x55 + } + } + KeyCode::KeyV => { + if cfg!(target_os = "macos") { + 0x09 + } else { + 0x56 + } + } + KeyCode::KeyW => { + if cfg!(target_os = "macos") { + 0x0D + } else { + 0x57 + } + } + KeyCode::KeyX => { + if cfg!(target_os = "macos") { + 0x07 + } else { + 0x58 + } + } + KeyCode::KeyY => { + if cfg!(target_os = "macos") { + 0x10 + } else { + 0x59 + } + } + KeyCode::KeyZ => { + if cfg!(target_os = "macos") { + 0x06 + } else { + 0x5A + } + } + + // Numbers + KeyCode::Digit0 => { + if cfg!(target_os = "macos") { + 0x1D + } else { + 0x30 + } + } + KeyCode::Digit1 => { + if cfg!(target_os = "macos") { + 0x12 + } else { + 0x31 + } + } + KeyCode::Digit2 => { + if cfg!(target_os = "macos") { + 0x13 + } else { + 0x32 + } + } + KeyCode::Digit3 => { + if cfg!(target_os = "macos") { + 0x14 + } else { + 0x33 + } + } + KeyCode::Digit4 => { + if cfg!(target_os = "macos") { + 0x15 + } else { + 0x34 + } + } + KeyCode::Digit5 => { + if cfg!(target_os = "macos") { + 0x17 + } else { + 0x35 + } + } + KeyCode::Digit6 => { + if cfg!(target_os = "macos") { + 0x16 + } else { + 0x36 + } + } + KeyCode::Digit7 => { + if cfg!(target_os = "macos") { + 0x1A + } else { + 0x37 + } + } + KeyCode::Digit8 => { + if cfg!(target_os = "macos") { + 0x1C + } else { + 0x38 + } + } + KeyCode::Digit9 => { + if cfg!(target_os = "macos") { + 0x19 + } else { + 0x39 + } + } + + // Function keys + KeyCode::F1 => { + if cfg!(target_os = "macos") { + 0x7A + } else { + 0x70 + } + } + KeyCode::F2 => { + if cfg!(target_os = "macos") { + 0x78 + } else { + 0x71 + } + } + KeyCode::F3 => { + if cfg!(target_os = "macos") { + 0x63 + } else { + 0x72 + } + } + KeyCode::F4 => { + if cfg!(target_os = "macos") { + 0x76 + } else { + 0x73 + } + } + KeyCode::F5 => { + if cfg!(target_os = "macos") { + 0x60 + } else { + 0x74 + } + } + KeyCode::F6 => { + if cfg!(target_os = "macos") { + 0x61 + } else { + 0x75 + } + } + KeyCode::F7 => { + if cfg!(target_os = "macos") { + 0x62 + } else { + 0x76 + } + } + KeyCode::F8 => { + if cfg!(target_os = "macos") { + 0x64 + } else { + 0x77 + } + } + KeyCode::F9 => { + if cfg!(target_os = "macos") { + 0x65 + } else { + 0x78 + } + } + KeyCode::F10 => { + if cfg!(target_os = "macos") { + 0x6D + } else { + 0x79 + } + } + KeyCode::F11 => { + if cfg!(target_os = "macos") { + 0x67 + } else { + 0x7A + } + } + KeyCode::F12 => { + if cfg!(target_os = "macos") { + 0x6F + } else { + 0x7B + } + } + + // Special keys + KeyCode::Enter => { + if cfg!(target_os = "macos") { + 0x24 + } else { + 0x0D + } + } + KeyCode::Space => { + if cfg!(target_os = "macos") { + 0x31 + } else { + 0x20 + } + } + KeyCode::Backspace => { + if cfg!(target_os = "macos") { + 0x33 + } else { + 0x08 + } + } + KeyCode::Delete => { + if cfg!(target_os = "macos") { + 0x75 + } else { + 0x2E + } + } + KeyCode::Tab => { + if cfg!(target_os = "macos") { + 0x30 + } else { + 0x09 + } + } + KeyCode::Escape => { + if cfg!(target_os = "macos") { + 0x35 + } else { + 0x1B + } + } + KeyCode::Insert => { + if cfg!(target_os = "macos") { + 0x72 + } else { + 0x2D + } + } + KeyCode::Home => { + if cfg!(target_os = "macos") { + 0x73 + } else { + 0x24 + } + } + KeyCode::End => { + if cfg!(target_os = "macos") { + 0x77 + } else { + 0x23 + } + } + KeyCode::PageUp => { + if cfg!(target_os = "macos") { + 0x74 + } else { + 0x21 + } + } + KeyCode::PageDown => { + if cfg!(target_os = "macos") { + 0x79 + } else { + 0x22 + } + } + + // Arrow keys + KeyCode::ArrowLeft => { + if cfg!(target_os = "macos") { + 0x7B + } else { + 0x25 + } + } + KeyCode::ArrowUp => { + if cfg!(target_os = "macos") { + 0x7E + } else { + 0x26 + } + } + KeyCode::ArrowRight => { + if cfg!(target_os = "macos") { + 0x7C + } else { + 0x27 + } + } + KeyCode::ArrowDown => { + if cfg!(target_os = "macos") { + 0x7D + } else { + 0x28 + } + } + + // Modifier keys + KeyCode::ShiftLeft => { + if cfg!(target_os = "macos") { + 0x38 + } else { + 0xA0 + } + } + KeyCode::ShiftRight => { + if cfg!(target_os = "macos") { + 0x3C + } else { + 0xA1 + } + } + KeyCode::ControlLeft => { + if cfg!(target_os = "macos") { + 0x3B + } else { + 0xA2 + } + } + KeyCode::ControlRight => { + if cfg!(target_os = "macos") { + 0x3E + } else { + 0xA3 + } + } + KeyCode::AltLeft => { + if cfg!(target_os = "macos") { + 0x3A + } else { + 0xA4 + } + } + KeyCode::AltRight => { + if cfg!(target_os = "macos") { + 0x3D + } else { + 0xA5 + } + } + KeyCode::SuperLeft => { + if cfg!(target_os = "macos") { + 0x37 + } else { + 0x5B + } + } + KeyCode::SuperRight => { + if cfg!(target_os = "macos") { + 0x36 + } else { + 0x5C + } + } + + // Lock keys + KeyCode::CapsLock => { + if cfg!(target_os = "macos") { + 0x39 + } else { + 0x14 + } + } + KeyCode::NumLock => { + if cfg!(target_os = "macos") { + 0x47 + } else { + 0x90 + } + } + KeyCode::ScrollLock => 0x91, + + // Punctuation + KeyCode::Semicolon => { + if cfg!(target_os = "macos") { + 0x29 + } else { + 0xBA + } + } + KeyCode::Equal => { + if cfg!(target_os = "macos") { + 0x18 + } else { + 0xBB + } + } + KeyCode::Comma => { + if cfg!(target_os = "macos") { + 0x2B + } else { + 0xBC + } + } + KeyCode::Minus => { + if cfg!(target_os = "macos") { + 0x1B + } else { + 0xBD + } + } + KeyCode::Period => { + if cfg!(target_os = "macos") { + 0x2F + } else { + 0xBE + } + } + KeyCode::Slash => { + if cfg!(target_os = "macos") { + 0x2C + } else { + 0xBF + } + } + KeyCode::Backquote => { + if cfg!(target_os = "macos") { + 0x32 + } else { + 0xC0 + } + } + KeyCode::BracketLeft => { + if cfg!(target_os = "macos") { + 0x21 + } else { + 0xDB + } + } + KeyCode::Backslash => { + if cfg!(target_os = "macos") { + 0x2A + } else { + 0xDC + } + } + KeyCode::BracketRight => { + if cfg!(target_os = "macos") { + 0x1E + } else { + 0xDD + } + } + KeyCode::Quote => { + if cfg!(target_os = "macos") { + 0x27 + } else { + 0xDE + } + } + + // Numpad + KeyCode::Numpad0 => { + if cfg!(target_os = "macos") { + 0x52 + } else { + 0x60 + } + } + KeyCode::Numpad1 => { + if cfg!(target_os = "macos") { + 0x53 + } else { + 0x61 + } + } + KeyCode::Numpad2 => { + if cfg!(target_os = "macos") { + 0x54 + } else { + 0x62 + } + } + KeyCode::Numpad3 => { + if cfg!(target_os = "macos") { + 0x55 + } else { + 0x63 + } + } + KeyCode::Numpad4 => { + if cfg!(target_os = "macos") { + 0x56 + } else { + 0x64 + } + } + KeyCode::Numpad5 => { + if cfg!(target_os = "macos") { + 0x57 + } else { + 0x65 + } + } + KeyCode::Numpad6 => { + if cfg!(target_os = "macos") { + 0x58 + } else { + 0x66 + } + } + KeyCode::Numpad7 => { + if cfg!(target_os = "macos") { + 0x59 + } else { + 0x67 + } + } + KeyCode::Numpad8 => { + if cfg!(target_os = "macos") { + 0x5B + } else { + 0x68 + } + } + KeyCode::Numpad9 => { + if cfg!(target_os = "macos") { + 0x5C + } else { + 0x69 + } + } + KeyCode::NumpadMultiply => { + if cfg!(target_os = "macos") { + 0x43 + } else { + 0x6A + } + } + KeyCode::NumpadAdd => { + if cfg!(target_os = "macos") { + 0x45 + } else { + 0x6B + } + } + KeyCode::NumpadSubtract => { + if cfg!(target_os = "macos") { + 0x4E + } else { + 0x6D + } + } + KeyCode::NumpadDecimal => { + if cfg!(target_os = "macos") { + 0x41 + } else { + 0x6E + } + } + KeyCode::NumpadDivide => { + if cfg!(target_os = "macos") { + 0x4B + } else { + 0x6F + } + } + + // Default case for unhandled keys + _ => 0, + } +} diff --git a/crates/bevy_cef_core/src/browser_process/client_handler.rs b/crates/bevy_cef_core/src/browser_process/client_handler.rs new file mode 100644 index 0000000..3c9a377 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/client_handler.rs @@ -0,0 +1,129 @@ +mod brp_handler; +mod js_emit_event_handler; + +use crate::browser_process::ContextMenuHandlerBuilder; +use crate::prelude::IntoString; +use cef::rc::{Rc, RcImpl}; +use cef::{ + Browser, Client, ContextMenuHandler, DisplayHandler, Frame, ImplClient, ImplProcessMessage, + ListValue, ProcessId, ProcessMessage, RenderHandler, WrapClient, sys, +}; +use std::os::raw::c_int; + +pub use brp_handler::BrpHandler; +pub use js_emit_event_handler::{IpcEventRaw, JsEmitEventHandler}; + +pub trait ProcessMessageHandler { + fn process_name(&self) -> &'static str; + + fn handle_message(&self, browser: &mut Browser, frame: &mut Frame, args: Option); +} + +/// ## Reference +/// +/// - [`CefBrowser Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefBrowser.html) +pub struct ClientHandlerBuilder { + object: *mut RcImpl, + render_handler: RenderHandler, + context_menu_handler: ContextMenuHandler, + message_handlers: Vec>, + display_handler: Option, +} + +impl ClientHandlerBuilder { + pub fn new(render_handler: RenderHandler) -> Self { + Self { + object: std::ptr::null_mut(), + render_handler, + context_menu_handler: ContextMenuHandlerBuilder::build(), + message_handlers: Vec::new(), + display_handler: None, + } + } + + pub fn with_display_handler(mut self, display_handler: DisplayHandler) -> Self { + self.display_handler = Some(display_handler); + self + } + + pub fn with_message_handler(mut self, handler: impl ProcessMessageHandler + 'static) -> Self { + self.message_handlers.push(std::rc::Rc::new(handler)); + self + } + + pub fn build(self) -> Client { + Client::new(self) + } +} + +impl Rc for ClientHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapClient for ClientHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for ClientHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + + Self { + object, + render_handler: self.render_handler.clone(), + context_menu_handler: self.context_menu_handler.clone(), + message_handlers: self.message_handlers.clone(), + display_handler: self.display_handler.clone(), + } + } +} + +impl ImplClient for ClientHandlerBuilder { + fn render_handler(&self) -> Option { + Some(self.render_handler.clone()) + } + + fn display_handler(&self) -> Option { + self.display_handler.clone() + } + + fn on_process_message_received( + &self, + browser: Option<&mut Browser>, + frame: Option<&mut Frame>, + _: ProcessId, + message: Option<&mut ProcessMessage>, + ) -> c_int { + if let Some(message) = message + && let Some(browser) = browser + && let Some(frame) = frame + && let Some(name) = Some(message.name().into_string()) + && let Some(handler) = self + .message_handlers + .iter() + .find(|h| h.process_name() == name.as_str()) + { + { + let args = message.argument_list(); + handler.handle_message(browser, frame, args); + } + }; + 1 + } + + #[inline] + fn get_raw(&self) -> *mut sys::_cef_client_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/browser_process/client_handler/brp_handler.rs b/crates/bevy_cef_core/src/browser_process/client_handler/brp_handler.rs new file mode 100644 index 0000000..06f2ea7 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/client_handler/brp_handler.rs @@ -0,0 +1,68 @@ +use crate::browser_process::client_handler::ProcessMessageHandler; +use crate::prelude::PROCESS_MESSAGE_BRP; +use crate::util::IntoString; +use async_channel::Sender; +use bevy::tasks::IoTaskPool; +use bevy_remote::{BrpMessage, BrpRequest}; +use cef::{ + Browser, Frame, ImplFrame, ImplListValue, ImplProcessMessage, ListValue, ProcessId, + process_message_create, +}; +use cef_dll_sys::cef_process_id_t; + +pub struct BrpHandler { + sender: Sender, +} + +impl BrpHandler { + pub const fn new(sender: Sender) -> Self { + Self { sender } + } +} + +impl ProcessMessageHandler for BrpHandler { + fn process_name(&self) -> &'static str { + PROCESS_MESSAGE_BRP + } + + fn handle_message(&self, _browser: &mut Browser, frame: &mut Frame, args: Option) { + if let Some(args) = args + && let Ok(request) = serde_json::from_str::(&args.string(1).into_string()) + { + let id = args.string(0).into_string(); + let frame = frame.clone(); + let brp_sender = self.sender.clone(); + IoTaskPool::get() + .spawn(async move { + let (tx, rx) = async_channel::unbounded(); + if brp_sender + .send(BrpMessage { + method: request.method, + params: request.params, + sender: tx, + }) + .await + .is_err() + { + return; + } + if let Ok(result) = rx.recv().await + && let Some(mut message) = + process_message_create(Some(&PROCESS_MESSAGE_BRP.into())) + && let Some(argument_list) = message.argument_list() + { + argument_list.set_string(0, Some(&id.as_str().into())); + argument_list.set_string( + 1, + Some(&serde_json::to_string(&result).unwrap().as_str().into()), + ); + frame.send_process_message( + ProcessId::from(cef_process_id_t::PID_RENDERER), + Some(&mut message), + ); + } + }) + .detach(); + } + } +} diff --git a/crates/bevy_cef_core/src/browser_process/client_handler/js_emit_event_handler.rs b/crates/bevy_cef_core/src/browser_process/client_handler/js_emit_event_handler.rs new file mode 100644 index 0000000..dbf36b2 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/client_handler/js_emit_event_handler.rs @@ -0,0 +1,39 @@ +use crate::browser_process::client_handler::ProcessMessageHandler; +use crate::prelude::{IntoString, PROCESS_MESSAGE_JS_EMIT}; +use async_channel::Sender; +use bevy::prelude::Entity; +use cef::{Browser, Frame, ImplListValue, ListValue}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct IpcEventRaw { + pub webview: Entity, + pub payload: String, +} + +pub struct JsEmitEventHandler { + webview: Entity, + sender: Sender, +} + +impl JsEmitEventHandler { + pub const fn new(webview: Entity, sender: Sender) -> Self { + Self { sender, webview } + } +} + +impl ProcessMessageHandler for JsEmitEventHandler { + fn process_name(&self) -> &'static str { + PROCESS_MESSAGE_JS_EMIT + } + + fn handle_message(&self, _browser: &mut Browser, _frame: &mut Frame, args: Option) { + if let Some(args) = args { + let event = IpcEventRaw { + webview: self.webview, // Placeholder, should be set correctly + payload: args.string(0).into_string(), + }; + let _ = self.sender.send_blocking(event); + } + } +} diff --git a/crates/bevy_cef_core/src/browser_process/context_menu_handler.rs b/crates/bevy_cef_core/src/browser_process/context_menu_handler.rs new file mode 100644 index 0000000..eda1363 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/context_menu_handler.rs @@ -0,0 +1,50 @@ +use cef::rc::{Rc, RcImpl}; +use cef::{ImplContextMenuHandler, WrapContextMenuHandler, sys}; + +/// ## Reference +/// +/// - [`CefContextMenuHandler Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefContextMenuHandler.html) +pub struct ContextMenuHandlerBuilder { + object: *mut RcImpl, +} + +impl ContextMenuHandlerBuilder { + pub fn build() -> cef::ContextMenuHandler { + cef::ContextMenuHandler::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl WrapContextMenuHandler for ContextMenuHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Rc for ContextMenuHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + core::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for ContextMenuHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { object } + } +} + +impl ImplContextMenuHandler for ContextMenuHandlerBuilder { + #[inline] + fn get_raw(&self) -> *mut sys::cef_context_menu_handler_t { + self.object as *mut sys::cef_context_menu_handler_t + } +} diff --git a/crates/bevy_cef_core/src/browser_process/display_handler.rs b/crates/bevy_cef_core/src/browser_process/display_handler.rs new file mode 100644 index 0000000..1969dec --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/display_handler.rs @@ -0,0 +1,165 @@ +use async_channel::Sender; +use bevy::log::{error, info, trace, warn}; +use bevy::window::SystemCursorIcon; +use cef::rc::{ConvertParam, Rc, RcImpl}; +use cef::{ + Browser, CefString, CursorInfo, CursorType, ImplDisplayHandler, LogSeverity, + WrapDisplayHandler, sys, +}; +use cef_dll_sys::{cef_cursor_type_t, cef_log_severity_t}; +use std::os::raw::c_int; + +pub type SystemCursorIconSenderInner = Sender; + +/// ## Reference +/// +/// - [`CefDisplayHandler Class Reference`](https://cef-builds.spotifycdn.com/docs/112.3/classCefDisplayHandler.html#af1cc8410a0b1a97166923428d3794636) +pub struct DisplayHandlerBuilder { + object: *mut RcImpl, + cursor_icon: SystemCursorIconSenderInner, +} + +impl DisplayHandlerBuilder { + pub fn build(cursor_icon: SystemCursorIconSenderInner) -> cef::DisplayHandler { + cef::DisplayHandler::new(Self { + object: core::ptr::null_mut(), + cursor_icon, + }) + } +} + +impl Rc for DisplayHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + core::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for DisplayHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + cursor_icon: self.cursor_icon.clone(), + } + } +} + +impl WrapDisplayHandler for DisplayHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl ImplDisplayHandler for DisplayHandlerBuilder { + fn on_console_message( + &self, + _: Option<&mut Browser>, + level: LogSeverity, + message: Option<&CefString>, + source: Option<&CefString>, + line: c_int, + ) -> c_int { + let message = format!( + "{}\nline:{line}\n{}", + source.map(|s| s.to_string()).unwrap_or_default(), + message.map(|m| m.to_string()).unwrap_or_default() + ); + match level.into_raw() { + cef_log_severity_t::LOGSEVERITY_ERROR => { + error!("{message}"); + } + cef_log_severity_t::LOGSEVERITY_WARNING => { + warn!("{message}"); + } + cef_log_severity_t::LOGSEVERITY_VERBOSE => { + trace!("{message}"); + } + _ => { + info!("{message}"); + } + } + 1 + } + + fn on_cursor_change( + &self, + _browser: Option<&mut Browser>, + _cursor: *mut u8, + type_: CursorType, + _: Option<&CursorInfo>, + ) -> c_int { + let _ = self + .cursor_icon + .send_blocking(to_system_cursor_icon(type_.into_raw())); + 1 + } + + #[inline] + fn get_raw(&self) -> *mut sys::cef_display_handler_t { + self.object.cast() + } +} + +pub fn to_system_cursor_icon(cursor_type: cef_dll_sys::cef_cursor_type_t) -> SystemCursorIcon { + match cursor_type { + cef_cursor_type_t::CT_POINTER => SystemCursorIcon::Default, + cef_cursor_type_t::CT_CROSS => SystemCursorIcon::Crosshair, + cef_cursor_type_t::CT_HAND => SystemCursorIcon::Pointer, + cef_cursor_type_t::CT_IBEAM => SystemCursorIcon::Text, + cef_cursor_type_t::CT_WAIT => SystemCursorIcon::Wait, + cef_cursor_type_t::CT_HELP => SystemCursorIcon::Help, + cef_cursor_type_t::CT_EASTRESIZE => SystemCursorIcon::EResize, + cef_cursor_type_t::CT_NORTHRESIZE => SystemCursorIcon::NResize, + cef_cursor_type_t::CT_NORTHEASTRESIZE => SystemCursorIcon::NeResize, + cef_cursor_type_t::CT_NORTHWESTRESIZE => SystemCursorIcon::NwResize, + cef_cursor_type_t::CT_SOUTHRESIZE => SystemCursorIcon::SResize, + cef_cursor_type_t::CT_SOUTHEASTRESIZE => SystemCursorIcon::SeResize, + cef_cursor_type_t::CT_SOUTHWESTRESIZE => SystemCursorIcon::SwResize, + cef_cursor_type_t::CT_WESTRESIZE => SystemCursorIcon::WResize, + cef_cursor_type_t::CT_NORTHSOUTHRESIZE => SystemCursorIcon::NsResize, + cef_cursor_type_t::CT_EASTWESTRESIZE => SystemCursorIcon::EwResize, + cef_cursor_type_t::CT_NORTHEASTSOUTHWESTRESIZE => SystemCursorIcon::NeswResize, + cef_cursor_type_t::CT_NORTHWESTSOUTHEASTRESIZE => SystemCursorIcon::NwseResize, + cef_cursor_type_t::CT_COLUMNRESIZE => SystemCursorIcon::ColResize, + cef_cursor_type_t::CT_ROWRESIZE => SystemCursorIcon::RowResize, + cef_cursor_type_t::CT_MIDDLEPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_EASTPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_NORTHPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_NORTHEASTPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_NORTHWESTPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_SOUTHPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_SOUTHEASTPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_SOUTHWESTPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_WESTPANNING => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_MOVE => SystemCursorIcon::Move, + cef_cursor_type_t::CT_VERTICALTEXT => SystemCursorIcon::VerticalText, + cef_cursor_type_t::CT_CELL => SystemCursorIcon::Cell, + cef_cursor_type_t::CT_CONTEXTMENU => SystemCursorIcon::ContextMenu, + cef_cursor_type_t::CT_ALIAS => SystemCursorIcon::Alias, + cef_cursor_type_t::CT_PROGRESS => SystemCursorIcon::Progress, + cef_cursor_type_t::CT_NODROP => SystemCursorIcon::NoDrop, + cef_cursor_type_t::CT_COPY => SystemCursorIcon::Copy, + cef_cursor_type_t::CT_NONE => SystemCursorIcon::Default, + cef_cursor_type_t::CT_NOTALLOWED => SystemCursorIcon::NotAllowed, + cef_cursor_type_t::CT_ZOOMIN => SystemCursorIcon::ZoomIn, + cef_cursor_type_t::CT_ZOOMOUT => SystemCursorIcon::ZoomOut, + cef_cursor_type_t::CT_GRAB => SystemCursorIcon::Grab, + cef_cursor_type_t::CT_GRABBING => SystemCursorIcon::Grabbing, + cef_cursor_type_t::CT_MIDDLE_PANNING_VERTICAL => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_MIDDLE_PANNING_HORIZONTAL => SystemCursorIcon::AllScroll, + cef_cursor_type_t::CT_CUSTOM => SystemCursorIcon::Default, + cef_cursor_type_t::CT_DND_NONE => SystemCursorIcon::Default, + cef_cursor_type_t::CT_DND_MOVE => SystemCursorIcon::Move, + cef_cursor_type_t::CT_DND_COPY => SystemCursorIcon::Copy, + cef_cursor_type_t::CT_DND_LINK => SystemCursorIcon::Alias, + cef_cursor_type_t::CT_NUM_VALUES => SystemCursorIcon::Default, + _ => SystemCursorIcon::Default, + } +} diff --git a/crates/bevy_cef_core/src/browser_process/localhost.rs b/crates/bevy_cef_core/src/browser_process/localhost.rs new file mode 100644 index 0000000..8a03e1e --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/localhost.rs @@ -0,0 +1,281 @@ +mod data_responser; +mod headers_responser; + +use crate::browser_process::localhost::data_responser::{DataResponser, parse_bytes_single_range}; +use crate::browser_process::localhost::headers_responser::HeadersResponser; +use crate::prelude::IntoString; +use async_channel::{Receiver, Sender}; +use bevy::asset::Asset; +use bevy::prelude::*; +use bevy::tasks::IoTaskPool; +use cef::rc::{Rc, RcImpl}; +use cef::{ + Browser, Callback, CefString, Frame, ImplCallback, ImplRequest, ImplResourceHandler, + ImplResponse, ImplSchemeHandlerFactory, Request, ResourceHandler, ResourceReadCallback, + Response, SchemeHandlerFactory, WrapResourceHandler, WrapSchemeHandlerFactory, sys, +}; +use cef_dll_sys::{_cef_resource_handler_t, cef_base_ref_counted_t}; +use serde::{Deserialize, Serialize}; +use std::os::raw::c_int; +use std::sync::{Arc, Mutex}; + +/// `cef://` scheme response asset. +#[derive(Asset, Reflect, Debug, Clone, Serialize, Deserialize)] +#[reflect(Debug, Serialize, Deserialize)] +pub struct CefResponse { + /// The media type. + pub mime_type: String, + /// The status code of the response, e.g., 200 for OK, 404 for Not Found. + pub status_code: u32, + /// The response data, typically HTML or other content. + pub data: Vec, +} + +impl Default for CefResponse { + fn default() -> Self { + Self { + mime_type: "text/html".to_string(), + status_code: 404, + data: b"

404 Not Found

".to_vec(), + } + } +} + +#[derive(Debug, Clone, Component)] +pub struct Responser(pub Sender); + +#[derive(Resource, Debug, Clone, Deref)] +pub struct Requester(pub Sender); + +#[derive(Resource, Debug, Clone)] +pub struct RequesterReceiver(pub Receiver); + +#[derive(Debug, Clone)] +pub struct CefRequest { + pub uri: String, + pub responser: Responser, +} + +/// Use to register a local schema handler for the CEF browser. +/// +/// ## Reference +/// +/// - [`CefSchemeHandlerFactory Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefSchemeHandlerFactory.html) +pub struct LocalSchemaHandlerBuilder { + object: *mut RcImpl, + requester: Requester, +} + +impl LocalSchemaHandlerBuilder { + pub fn build(requester: Requester) -> SchemeHandlerFactory { + SchemeHandlerFactory::new(Self { + object: std::ptr::null_mut(), + requester, + }) + } +} + +impl Rc for LocalSchemaHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapSchemeHandlerFactory for LocalSchemaHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for LocalSchemaHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + requester: self.requester.clone(), + } + } +} + +impl ImplSchemeHandlerFactory for LocalSchemaHandlerBuilder { + fn create( + &self, + _browser: Option<&mut Browser>, + _frame: Option<&mut Frame>, + _scheme_name: Option<&CefString>, + _request: Option<&mut Request>, + ) -> Option { + Some(LocalResourceHandlerBuilder::build(self.requester.clone())) + } + + #[inline] + fn get_raw(&self) -> *mut sys::_cef_scheme_handler_factory_t { + self.object.cast() + } +} + +struct LocalResourceHandlerBuilder { + object: *mut RcImpl<_cef_resource_handler_t, Self>, + requester: Requester, + headers: Arc>, + data: Arc>, +} + +impl LocalResourceHandlerBuilder { + fn build(requester: Requester) -> ResourceHandler { + ResourceHandler::new(Self { + object: std::ptr::null_mut(), + requester, + headers: Arc::new(Mutex::new(HeadersResponser::default())), + data: Arc::new(Mutex::new(DataResponser::default())), + }) + } +} + +impl WrapResourceHandler for LocalResourceHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for LocalResourceHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + requester: self.requester.clone(), + headers: self.headers.clone(), + data: self.data.clone(), + } + } +} + +impl Rc for LocalResourceHandlerBuilder { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl ImplResourceHandler for LocalResourceHandlerBuilder { + fn open( + &self, + request: Option<&mut Request>, + handle_request: Option<&mut c_int>, + callback: Option<&mut Callback>, + ) -> c_int { + let Some(request) = request else { + // Cancel the request if no request is provided + return 0; + }; + let range_header_value = request.header_by_name(Some(&"Range".into())).into_string(); + let range = parse_bytes_single_range(&range_header_value); + let Some(callback) = callback.cloned() else { + // If no callback is provided, we cannot handle the request + return 0; + }; + if let Some(handle_request) = handle_request { + *handle_request = 0; + } + let url = request.url().into_string(); + let requester = self.requester.clone(); + let headers_responser = self.headers.clone(); + let data_responser = self.data.clone(); + IoTaskPool::get() + .spawn(async move { + let (tx, rx) = async_channel::bounded(1); + let _ = requester + .send(CefRequest { + uri: url + .strip_prefix("cef://localhost/") + .unwrap_or_default() + .to_string(), + responser: Responser(tx), + }) + .await; + let response = rx.recv().await.unwrap_or_default(); + headers_responser.lock().unwrap().prepare(&response, &range); + data_responser + .lock() + .unwrap() + .prepare(response.data, &range); + callback.cont(); + }) + .detach(); + 1 + } + + fn response_headers( + &self, + response: Option<&mut Response>, + response_length: Option<&mut i64>, + _redirect_url: Option<&mut CefString>, + ) { + let Ok(responser) = self.headers.lock() else { + return; + }; + if let Some(response) = response { + response.set_mime_type(Some(&responser.mime_type.as_str().into())); + response.set_status(responser.status_code as _); + for (name, value) in &responser.headers { + response.set_header_by_name( + Some(&name.as_str().into()), + Some(&value.as_str().into()), + false as _, + ); + } + } + if let Some(response_length) = response_length { + *response_length = responser.response_length as _; + } + } + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + fn read( + &self, + data_out: *mut u8, + bytes_to_read: c_int, + bytes_read: Option<&mut c_int>, + _: Option<&mut ResourceReadCallback>, + ) -> c_int { + let Some(bytes_read) = bytes_read else { + // If no bytes_read is provided, we cannot read data + return 0; + }; + let Ok(mut responser) = self.data.lock() else { + return 0; + }; + match responser.read(bytes_to_read as _) { + Some(data) if !data.is_empty() => { + let n = data.len(); + unsafe { + std::ptr::copy_nonoverlapping(data.as_ptr(), data_out, n); + } + *bytes_read = n as i32; + 1 + } + _ => { + *bytes_read = 0; + 0 + } + } + } + + #[inline] + fn get_raw(&self) -> *mut _cef_resource_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/browser_process/localhost/data_responser.rs b/crates/bevy_cef_core/src/browser_process/localhost/data_responser.rs new file mode 100644 index 0000000..cf6e1e2 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/localhost/data_responser.rs @@ -0,0 +1,236 @@ +use bevy::prelude::*; + +#[derive(Default, Clone)] +pub struct DataResponser { + data: Vec, + offset: usize, + end_offset: usize, +} + +impl DataResponser { + /// Prepares the data and headers for the response. + /// + /// The range header values only support the `bytes` range unit type and single range. + /// TODO: Support multiple ranges. + pub fn prepare(&mut self, data: Vec, range: &Option<(usize, Option)>) { + if let Some((start, end)) = range { + self.offset = *start; + self.end_offset = end.unwrap_or(data.len() - 1) + 1; + self.data = data; + } else { + self.offset = 0; + self.end_offset = data.len(); + self.data = data; + } + } + + pub fn read(&mut self, bytes_to_read: isize) -> Option<&[u8]> { + if self.offset >= self.data.len() { + return None; + } + let start = self.offset; + let end = if bytes_to_read < 0 { + self.data.len() + } else { + (self.offset as isize + bytes_to_read) as usize + }; + let end = end.min(self.end_offset); + + if start >= end || start >= self.data.len() { + return None; + } + + let slice = &self.data[start..end.min(self.data.len())]; + self.offset += slice.len(); + Some(slice) + } +} + +pub fn parse_bytes_single_range(range_header_value: &str) -> Option<(usize, Option)> { + let ranges = parse_bytes_range(range_header_value)?; + ranges.first().cloned() +} + +/// Parses the `Range` header value from a request and returns the start of the range. +/// +/// ## Reference +/// +/// - [`Range_requests`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests) +fn parse_bytes_range(range_header_value: &str) -> Option)>> { + if !range_header_value.starts_with("bytes=") { + return None; + } + let mut ranges = Vec::new(); + let value = range_header_value.trim_start_matches("bytes="); + // bytes=100-200,300-400 => ["100-200", "300-400"] + let byte_ranges = value.split(","); + for range in byte_ranges { + // 100-200 => ["100", "200"] + let mut split = range.split("-"); + let start = split.next()?; + let end = split.next(); + let start = start.parse::().ok()?; + let end = end.and_then(|e| e.parse::().ok()); + ranges.push((start, end)); + } + Some(ranges) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn range_start_is_none_if_empty() { + assert_eq!(parse_bytes_range(""), None); + } + + #[test] + fn range_only_start_offset() { + assert_eq!(parse_bytes_range("bytes=100-"), Some(vec![(100, None)])); + } + + #[test] + fn range_one_bytes() { + assert_eq!( + parse_bytes_range("bytes=100-200"), + Some(vec![(100, Some(200))]) + ); + } + + #[test] + fn range_multiple_ranges() { + assert_eq!( + parse_bytes_range("bytes=100-200,300-400"), + Some(vec![(100, Some(200)), (300, Some(400))]) + ); + } + + #[test] + fn data_responser_new_with_start_and_end() { + let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let mut responser = DataResponser::default(); + responser.prepare(data.clone(), &Some((2, Some(7)))); + assert_eq!(responser.data, data); + assert_eq!(responser.offset, 2); + assert_eq!(responser.end_offset, 7); + } + + #[test] + fn data_responser_new_with_start_only() { + let data = vec![1, 2, 3, 4, 5]; + let mut responser = DataResponser::default(); + responser.prepare(data.clone(), &Some((3, None))); + + assert_eq!(responser.data, data); + assert_eq!(responser.offset, 3); + assert_eq!(responser.end_offset, 5); + } + + #[test] + fn data_responser_new_with_zero_start() { + let data = vec![1, 2, 3]; + let mut responser = DataResponser::default(); + responser.prepare(data.clone(), &Some((0, None))); + + assert_eq!(responser.data, data); + assert_eq!(responser.offset, 0); + assert_eq!(responser.end_offset, 2); + } + + #[test] + fn data_responser_new_with_empty_data() { + let data = vec![]; + let mut responser = DataResponser::default(); + responser.prepare(data.clone(), &Some((0, None))); + + assert_eq!(responser.data, data); + assert_eq!(responser.offset, 0); + assert_eq!(responser.end_offset, 0); + } + + #[test] + fn data_responser_new_with_start_beyond_data_length() { + let data = vec![1, 2, 3]; + let mut responser = DataResponser::default(); + responser.prepare(data.clone(), &Some((5, None))); + + assert_eq!(responser.data, data); + assert_eq!(responser.offset, 5); + assert_eq!(responser.end_offset, 3); + } + + #[test] + fn data_responser_new_with_end_beyond_data_length() { + let data = vec![1, 2, 3]; + let mut responser = DataResponser::default(); + responser.prepare(data.clone(), &Some((1, Some(10)))); + + assert_eq!(responser.data, data); + assert_eq!(responser.offset, 1); + assert_eq!(responser.end_offset, 10); + } + + #[test] + fn data_responser_read_no_end_data_smaller_than_bytes_to_read() { + let data = vec![1, 2, 3, 4, 5]; + let mut responser = DataResponser::default(); + responser.prepare(data, &Some((2, None))); + + let result = responser.read(10); + assert_eq!(result, Some(&[3, 4, 5][..])); + } + + #[test] + fn data_responser_read_no_end_data_larger_than_bytes_to_read() { + let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let mut responser = DataResponser::default(); + responser.prepare(data, &Some((2, None))); + + let result1 = responser.read(3); + assert_eq!(result1, Some(&[3, 4, 5][..])); + + let result2 = responser.read(3); + assert_eq!(result2, Some(&[6, 7, 8][..])); + } + + #[test] + fn data_responser_read_with_end_data_smaller_than_bytes_to_read() { + let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let mut responser = DataResponser::default(); + responser.prepare(data, &Some((2, Some(6)))); + + let result = responser.read(10); + assert_eq!(result, Some(&[3, 4, 5, 6][..])); + } + + #[test] + fn data_responser_read_with_end_data_larger_than_bytes_to_read() { + let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let mut responser = DataResponser::default(); + responser.prepare(data, &Some((1, Some(7)))); + + let result1 = responser.read(3); + assert_eq!(result1, Some(&[2, 3, 4][..])); + + let result2 = responser.read(3); + assert_eq!(result2, Some(&[5, 6, 7][..])); + } + #[test] + fn data_responser_read_consecutive_calls_until_end() { + let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; + let mut responser = DataResponser::default(); + responser.prepare(data, &Some((1, Some(6)))); + + let result1 = responser.read(2); + assert_eq!(result1, Some(&[2, 3][..])); + + let result2 = responser.read(2); + assert_eq!(result2, Some(&[4, 5][..])); + + let result3 = responser.read(2); + assert_eq!(result3, Some(&[6][..])); + + let result4 = responser.read(2); + assert_eq!(result4, None); + } +} diff --git a/crates/bevy_cef_core/src/browser_process/localhost/headers_responser.rs b/crates/bevy_cef_core/src/browser_process/localhost/headers_responser.rs new file mode 100644 index 0000000..46d3ef7 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/localhost/headers_responser.rs @@ -0,0 +1,236 @@ +use crate::prelude::CefResponse; + +#[derive(Clone, Default, Debug)] +pub struct HeadersResponser { + pub mime_type: String, + pub status_code: u32, + pub headers: Vec<(String, String)>, + pub response_length: usize, +} + +impl HeadersResponser { + pub fn prepare(&mut self, cef_response: &CefResponse, range: &Option<(usize, Option)>) { + self.mime_type = cef_response.mime_type.clone(); + self.status_code = if range.is_some() { + 206 // Partial Content + } else { + cef_response.status_code + }; + self.headers.clear(); + self.response_length = obtain_response_length(&cef_response.data, range); + if let Some(content_range) = content_range_header_value(&cef_response.data, range) { + self.headers + .push(("Content-Range".to_string(), content_range)); + self.headers + .push(("Accept-Ranges".to_string(), "bytes".to_string())); + } + } +} + +/// Create a `Content-Range` header value based on the provided data and range. +/// +/// If the range is `None`, since the request type is not a range request, it returns `None`. +/// +/// ## Reference +/// +/// - [206 Partial Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/206) +fn content_range_header_value( + data: &[u8], + range: &Option<(usize, Option)>, +) -> Option { + let (start, end) = range.as_ref()?; + Some(format!( + "bytes {}-{}/{}", + start, + end.unwrap_or(data.len() - 1), + data.len() + )) +} + +fn obtain_response_length(data: &[u8], range: &Option<(usize, Option)>) -> usize { + match range { + Some((start, end)) => end.unwrap_or(data.len() - 1) - start + 1, + None => data.len(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bevy::utils::default; + + #[test] + fn test_obtain_response_length_no_range() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &None); + assert_eq!(result, 13); + } + + #[test] + fn test_obtain_response_length_empty_data_no_range() { + let data = b""; + let result = obtain_response_length(data, &None); + assert_eq!(result, 0); + } + + #[test] + fn test_obtain_response_length_range_with_end() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &Some((0, Some(5)))); + assert_eq!(result, 5); + } + + #[test] + fn test_obtain_response_length_range_partial() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &Some((7, Some(12)))); + assert_eq!(result, 5); + } + + #[test] + fn test_obtain_response_length_range_without_end() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &Some((7, None))); + assert_eq!(result, 6); + } + + #[test] + fn test_obtain_response_length_range_from_start() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &Some((0, None))); + assert_eq!(result, 13); + } + + #[test] + fn test_obtain_response_length_range_zero_length() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &Some((5, Some(5)))); + assert_eq!(result, 0); + } + + #[test] + fn test_obtain_response_length_range_end_equals_data_len() { + let data = b"Hello, World!"; + let result = obtain_response_length(data, &Some((0, Some(13)))); + assert_eq!(result, 13); + } + + #[test] + fn test_obtain_response_length_empty_data_with_range() { + let data = b""; + let result = obtain_response_length(data, &Some((0, None))); + assert_eq!(result, 0); + } + + #[test] + fn test_obtain_response_length_large_data() { + let data = vec![0u8; 1024]; + let result = obtain_response_length(&data, &None); + assert_eq!(result, 1024); + } + + #[test] + fn test_obtain_response_length_large_data_with_range() { + let data = vec![0u8; 1024]; + let result = obtain_response_length(&data, &Some((100, Some(200)))); + assert_eq!(result, 100); + } + + #[test] + fn test_content_range_header_value_no_range() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &None); + assert_eq!(result, None); + } + + #[test] + fn test_content_range_header_value_range_with_end() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((0, Some(5)))); + assert_eq!(result, Some("bytes 0-4/13".to_string())); + } + + #[test] + fn test_content_range_header_value_range_without_end() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((7, None))); + assert_eq!(result, Some("bytes 7-12/13".to_string())); + } + + #[test] + fn test_content_range_header_value_range_from_start() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((0, None))); + assert_eq!(result, Some("bytes 0-12/13".to_string())); + } + + #[test] + fn test_content_range_header_value_range_partial() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((7, Some(12)))); + assert_eq!(result, Some("bytes 7-11/13".to_string())); + } + + #[test] + fn test_content_range_header_value_range_single_byte() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((5, Some(6)))); + assert_eq!(result, Some("bytes 5-5/13".to_string())); + } + + #[test] + fn test_content_range_header_value_range_last_byte() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((12, Some(13)))); + assert_eq!(result, Some("bytes 12-12/13".to_string())); + } + + #[test] + fn test_content_range_header_value_single_byte_data() { + let data = b"a"; + let result = content_range_header_value(data, &Some((0, None))); + assert_eq!(result, Some("bytes 0-0/1".to_string())); + } + + #[test] + fn test_content_range_header_value_large_data() { + let data = vec![0u8; 1024]; + let result = content_range_header_value(&data, &Some((100, Some(200)))); + assert_eq!(result, Some("bytes 100-199/1024".to_string())); + } + + #[test] + fn test_content_range_header_value_large_data_no_end() { + let data = vec![0u8; 1024]; + let result = content_range_header_value(&data, &Some((500, None))); + assert_eq!(result, Some("bytes 500-1023/1024".to_string())); + } + + #[test] + fn test_content_range_header_value_zero_start() { + let data = b"test"; + let result = content_range_header_value(data, &Some((0, Some(2)))); + assert_eq!(result, Some("bytes 0-1/4".to_string())); + } + + #[test] + fn test_content_range_header_value_range_end_equals_data_len() { + let data = b"Hello, World!"; + let result = content_range_header_value(data, &Some((0, Some(13)))); + assert_eq!(result, Some("bytes 0-12/13".to_string())); + } + + #[test] + fn status_code_is_206_for_partial_content() { + let data = b"Hello, World!"; + let mut headers_responser = HeadersResponser::default(); + headers_responser.prepare( + &CefResponse { + data: data.to_vec(), + ..default() + }, + &Some((0, Some(5))), + ); + assert_eq!(headers_responser.status_code, 206); + } +} diff --git a/crates/bevy_cef_core/src/browser_process/renderer_handler.rs b/crates/bevy_cef_core/src/browser_process/renderer_handler.rs new file mode 100644 index 0000000..56c0702 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/renderer_handler.rs @@ -0,0 +1,167 @@ +use async_channel::{Receiver, Sender}; +use bevy::prelude::{Entity, Event}; +use cef::rc::{Rc, RcImpl}; +use cef::{ + Browser, CefString, ImplRenderHandler, PaintElementType, Range, Rect, RenderHandler, + WrapRenderHandler, sys, +}; +use cef_dll_sys::cef_paint_element_type_t; +use std::cell::Cell; +use std::os::raw::c_int; + +pub type TextureSender = Sender; + +pub type TextureReceiver = Receiver; + +/// The texture structure passed from [`CefRenderHandler::OnPaint`](https://cef-builds.spotifycdn.com/docs/106.1/classCefRenderHandler.html#a6547d5c9dd472e6b84706dc81d3f1741). +#[derive(Debug, Clone, PartialEq, Event)] +pub struct RenderTexture { + /// The entity of target rendering webview. + pub webview: Entity, + /// The type of the paint element. + pub ty: RenderPaintElementType, + /// The width of the texture. + pub width: u32, + /// The height of the texture. + pub height: u32, + /// This buffer will be `width` *`height` * 4 bytes in size and represents a BGRA image with an upper-left origin + pub buffer: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RenderPaintElementType { + /// The main frame of the browser. + View, + /// The popup frame of the browser. + Popup, +} + +pub type SharedViewSize = std::rc::Rc>; +pub type SharedImeCaret = std::rc::Rc>; + +/// ## Reference +/// +/// - [`CefRenderHandler Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefRenderHandler.html) +pub struct RenderHandlerBuilder { + object: *mut RcImpl, + webview: Entity, + texture_sender: TextureSender, + size: SharedViewSize, + ime_caret: SharedImeCaret, +} + +impl RenderHandlerBuilder { + pub fn build( + webview: Entity, + texture_sender: TextureSender, + size: SharedViewSize, + ime_caret: SharedImeCaret, + ) -> RenderHandler { + RenderHandler::new(Self { + object: std::ptr::null_mut(), + webview, + texture_sender, + size, + ime_caret, + }) + } +} + +impl Rc for RenderHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapRenderHandler for RenderHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for RenderHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + webview: self.webview, + texture_sender: self.texture_sender.clone(), + size: self.size.clone(), + ime_caret: self.ime_caret.clone(), + } + } +} + +impl ImplRenderHandler for RenderHandlerBuilder { + fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) { + if let Some(rect) = rect { + let size = self.size.get(); + rect.width = size.x as _; + rect.height = size.y as _; + } + } + + fn on_text_selection_changed( + &self, + _browser: Option<&mut Browser>, + _: Option<&CefString>, + selected_range: Option<&Range>, + ) { + if let Some(selected_range) = selected_range { + self.ime_caret.set(selected_range.to); + } + } + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + fn on_paint( + &self, + _browser: Option<&mut Browser>, + type_: PaintElementType, + _dirty_rects_count: usize, + _dirty_rects: Option<&Rect>, + buffer: *const u8, + width: c_int, + height: c_int, + ) { + let ty = match type_.as_ref() { + cef_paint_element_type_t::PET_POPUP => RenderPaintElementType::Popup, + _ => RenderPaintElementType::View, + }; + let texture = RenderTexture { + webview: self.webview, + ty, + width: width as u32, + height: height as u32, + buffer: unsafe { + std::slice::from_raw_parts(buffer, (width * height * 4) as usize).to_vec() + }, + }; + let _ = self.texture_sender.send_blocking(texture); + } + + /// MEMO: This method only supports on Windows + /// + /// In Windows, this method is more performant than `on_paint`? + #[cfg(target_os = "windows")] + fn on_accelerated_paint( + &self, + _browser: Option<&mut Browser>, + _type_: PaintElementType, + _dirty_rects_count: usize, + _dirty_rects: Option<&Rect>, + _: Option<&AcceleratedPaintInfo>, + ) { + } + + #[inline] + fn get_raw(&self) -> *mut sys::_cef_render_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/browser_process/request_context_handler.rs b/crates/bevy_cef_core/src/browser_process/request_context_handler.rs new file mode 100644 index 0000000..78ab745 --- /dev/null +++ b/crates/bevy_cef_core/src/browser_process/request_context_handler.rs @@ -0,0 +1,50 @@ +use cef::rc::{Rc, RcImpl}; +use cef::{ImplRequestContextHandler, WrapRequestContextHandler, sys}; + +/// ## Reference +/// +/// - [`CefRequestContextHandler Class Reference`](https://cef-builds.spotifycdn.com/docs/106.1/classCefRequestContextHandler.html) +pub struct RequestContextHandlerBuilder { + object: *mut RcImpl, +} + +impl RequestContextHandlerBuilder { + pub fn build() -> cef::RequestContextHandler { + cef::RequestContextHandler::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl WrapRequestContextHandler for RequestContextHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Rc for RequestContextHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + core::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for RequestContextHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { object } + } +} + +impl ImplRequestContextHandler for RequestContextHandlerBuilder { + #[inline] + fn get_raw(&self) -> *mut sys::_cef_request_context_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/debug.rs b/crates/bevy_cef_core/src/debug.rs new file mode 100644 index 0000000..1e97dbd --- /dev/null +++ b/crates/bevy_cef_core/src/debug.rs @@ -0,0 +1,52 @@ +use cef::{load_library, unload_library}; +use std::env::home_dir; + +/// This loader is a modified version of [LibraryLoader](cef::library_loader::LibraryLoader) that can load the framework located in the home directory. +pub struct DebugLibraryLoader { + path: std::path::PathBuf, +} + +impl Default for DebugLibraryLoader { + fn default() -> Self { + Self::new() + } +} + +impl DebugLibraryLoader { + const FRAMEWORK_PATH: &'static str = + "Chromium Embedded Framework.framework/Chromium Embedded Framework"; + + pub fn new() -> Self { + let path = home_dir() + .unwrap() + .join(".local") + .join("share") + .join("cef") + .join(Self::FRAMEWORK_PATH) + .canonicalize() + .unwrap(); + + Self { path } + } + + // See [cef_load_library] for more documentation. + pub fn load(&self) -> bool { + Self::load_library(&self.path) + } + + fn load_library(name: &std::path::Path) -> bool { + use std::os::unix::ffi::OsStrExt; + let Ok(name) = std::ffi::CString::new(name.as_os_str().as_bytes()) else { + return false; + }; + unsafe { load_library(Some(&*name.as_ptr().cast())) == 1 } + } +} + +impl Drop for DebugLibraryLoader { + fn drop(&mut self) { + if unload_library() != 1 { + eprintln!("cannot unload framework {}", self.path.display()); + } + } +} diff --git a/crates/bevy_cef_core/src/lib.rs b/crates/bevy_cef_core/src/lib.rs new file mode 100644 index 0000000..a50b869 --- /dev/null +++ b/crates/bevy_cef_core/src/lib.rs @@ -0,0 +1,15 @@ +mod browser_process; +mod debug; +mod render_process; +mod util; + +pub mod prelude { + pub use crate::browser_process::*; + pub use crate::debug::*; + pub use crate::render_process::app::*; + pub use crate::render_process::brp::*; + pub use crate::render_process::emit::*; + pub use crate::render_process::execute_render_process; + pub use crate::render_process::render_process_handler::*; + pub use crate::util::*; +} diff --git a/crates/bevy_cef_core/src/render_process.rs b/crates/bevy_cef_core/src/render_process.rs new file mode 100644 index 0000000..bebbef5 --- /dev/null +++ b/crates/bevy_cef_core/src/render_process.rs @@ -0,0 +1,28 @@ +use crate::prelude::RenderProcessAppBuilder; +use cef::args::Args; +use cef::{api_hash, execute_process, sys}; + +pub mod app; +pub mod brp; +pub mod emit; +pub mod listen; +pub mod render_process_handler; + +/// Execute the CEF render process. +pub fn execute_render_process() { + let args = Args::new(); + #[cfg(target_os = "macos")] + let _loader = { + let loader = + cef::library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), true); + assert!(loader.load()); + loader + }; + let _ = api_hash(sys::CEF_API_VERSION_LAST, 0); + let mut app = RenderProcessAppBuilder::build(); + execute_process( + Some(args.as_main_args()), + Some(&mut app), + std::ptr::null_mut(), + ); +} diff --git a/crates/bevy_cef_core/src/render_process/app.rs b/crates/bevy_cef_core/src/render_process/app.rs new file mode 100644 index 0000000..8cd450c --- /dev/null +++ b/crates/bevy_cef_core/src/render_process/app.rs @@ -0,0 +1,63 @@ +use crate::prelude::RenderProcessHandlerBuilder; +use crate::util::{SCHEME_CEF, cef_scheme_flags}; +use cef::rc::{Rc, RcImpl}; +use cef::{ImplApp, ImplSchemeRegistrar, RenderProcessHandler, SchemeRegistrar, WrapApp}; +use cef_dll_sys::{_cef_app_t, cef_base_ref_counted_t}; + +#[derive(Default)] +pub struct RenderProcessAppBuilder { + object: *mut RcImpl<_cef_app_t, Self>, +} + +impl RenderProcessAppBuilder { + pub fn build() -> cef::App { + cef::App::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl Clone for RenderProcessAppBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + self.object + }; + Self { object } + } +} + +impl Rc for RenderProcessAppBuilder { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl ImplApp for RenderProcessAppBuilder { + fn render_process_handler(&self) -> Option { + Some(RenderProcessHandler::new( + RenderProcessHandlerBuilder::build(), + )) + } + + fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) { + if let Some(registrar) = registrar { + registrar.add_custom_scheme(Some(&SCHEME_CEF.into()), cef_scheme_flags() as _); + } + } + + #[inline] + fn get_raw(&self) -> *mut _cef_app_t { + self.object as *mut _cef_app_t + } +} + +impl WrapApp for RenderProcessAppBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) { + self.object = object; + } +} diff --git a/crates/bevy_cef_core/src/render_process/brp.rs b/crates/bevy_cef_core/src/render_process/brp.rs new file mode 100644 index 0000000..c310cf4 --- /dev/null +++ b/crates/bevy_cef_core/src/render_process/brp.rs @@ -0,0 +1,181 @@ +use crate::prelude::{BRP_PROMISES, PROCESS_MESSAGE_BRP, v8_value_to_json}; +use cef::rc::{ConvertParam, ConvertReturnValue, Rc, RcImpl}; +use cef::{ + CefString, Frame, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Handler, ImplV8Value, + ProcessId, V8Handler, V8Value, WrapV8Handler, process_message_create, sys, + v8_value_create_promise, v8_value_create_string, +}; +use cef_dll_sys::{_cef_v8_handler_t, _cef_v8_value_t, cef_process_id_t, cef_string_t}; +use std::os::raw::c_int; +use std::ptr::write; + +/// Implements the `window.brp` function in JavaScript. +/// +/// The function definition is `async (request: BrpRequest) -> Promise`. +/// +/// The flow from the execution of the function to the return of the result to the Javascript side is as follows: +/// 1. Send `BrpRequest` to the browser process. +/// 2. The browser process receives `BrpResult` and sends it to the render process. +/// 3. The render process receives the result in `on_process_message_received`. +/// 4. The render process resolves the result to the `Promise` of `window.brp`. +pub struct BrpBuilder { + object: *mut RcImpl<_cef_v8_handler_t, Self>, + frame: Frame, +} + +impl BrpBuilder { + pub fn build(frame: Frame) -> V8Handler { + V8Handler::new(Self { + object: core::ptr::null_mut(), + frame, + }) + } +} + +impl Rc for BrpBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapV8Handler for BrpBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_v8_handler_t, Self>) { + self.object = object; + } +} + +impl Clone for BrpBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + frame: self.frame.clone(), + } + } +} + +impl ImplV8Handler for BrpBuilder { + fn execute( + &self, + _: Option<&CefString>, + _: Option<&mut V8Value>, + arguments: Option<&[Option]>, + ret: Option<&mut Option>, + _: Option<&mut CefString>, + ) -> c_int { + if let Some(mut process) = process_message_create(Some(&PROCESS_MESSAGE_BRP.into())) + && let Some(promise) = v8_value_create_promise() + { + if let Some(arguments_list) = process.argument_list() + && let Some(arguments) = arguments + && let Some(Some(arg)) = arguments.first() + && let Some(brp_request) = v8_value_to_json(arg) + && let Ok(brp_request) = serde_json::to_string(&brp_request) + && let Some(ret) = ret + { + let id = uuid::Uuid::new_v4().to_string(); + arguments_list.set_string(0, Some(&id.as_str().into())); + arguments_list.set_string(1, Some(&brp_request.as_str().into())); + self.frame.send_process_message( + ProcessId::from(cef_process_id_t::PID_BROWSER), + Some(&mut process), + ); + ret.replace(promise.clone()); + let mut promises = BRP_PROMISES.lock().unwrap(); + promises.insert(id, promise); + } else { + let mut exception = + v8_value_create_string(Some(&"Failed to execute BRP request".into())); + promise.resolve_promise(exception.as_mut()); + } + } + 1 + } + + fn init_methods(object: &mut _cef_v8_handler_t) { + init_methods::(object); + } + + fn get_raw(&self) -> *mut _cef_v8_handler_t { + self.object.cast() + } +} + +fn init_methods(object: &mut _cef_v8_handler_t) { + object.execute = Some(execute::); +} + +extern "C" fn execute( + self_: *mut _cef_v8_handler_t, + name: *const cef_string_t, + object: *mut _cef_v8_value_t, + arguments_count: usize, + arguments: *const *mut _cef_v8_value_t, + retval: *mut *mut _cef_v8_value_t, + exception: *mut cef_string_t, +) -> c_int { + let (arg_self_, arg_name, arg_object, arg_arguments_count, arg_arguments, _, arg_exception) = ( + self_, + name, + object, + arguments_count, + arguments, + retval, + exception, + ); + let arg_self_: &RcImpl<_, I> = RcImpl::get(arg_self_); + let arg_name = if arg_name.is_null() { + None + } else { + Some(arg_name.into()) + }; + let arg_name = arg_name.as_ref(); + let mut arg_object = + unsafe { arg_object.as_mut() }.map(|arg| (arg as *mut _cef_v8_value_t).wrap_result()); + let arg_object = arg_object.as_mut(); + let vec_arguments = unsafe { arg_arguments.as_ref() }.map(|arg| { + let arg = + unsafe { std::slice::from_raw_parts(std::ptr::from_ref(arg), arg_arguments_count) }; + arg.iter() + .map(|arg| { + if arg.is_null() { + None + } else { + Some((*arg).wrap_result()) + } + }) + .collect::>() + }); + let arg_arguments = vec_arguments.as_deref(); + let mut arg_retval: Option = None; + let arg = Some(&mut arg_retval); + let mut arg_exception = if arg_exception.is_null() { + None + } else { + Some(arg_exception.into()) + }; + let arg_exception = arg_exception.as_mut(); + let r = ImplV8Handler::execute( + &arg_self_.interface, + arg_name, + arg_object, + arg_arguments, + arg, + arg_exception, + ); + if let Some(ret) = arg_retval { + // When the result is received, the pointer should be updated here + // and the exception should also be updated. + unsafe { + write(retval, ret.into_raw()); + } + } + r +} diff --git a/crates/bevy_cef_core/src/render_process/emit.rs b/crates/bevy_cef_core/src/render_process/emit.rs new file mode 100644 index 0000000..6f55f05 --- /dev/null +++ b/crates/bevy_cef_core/src/render_process/emit.rs @@ -0,0 +1,82 @@ +use crate::prelude::PROCESS_MESSAGE_JS_EMIT; +use crate::util::v8_value_to_json; +use cef::rc::{Rc, RcImpl}; +use cef::{ + CefString, Frame, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Handler, ProcessId, + V8Handler, V8Value, WrapV8Handler, process_message_create, sys, +}; +use cef_dll_sys::cef_process_id_t; +use std::os::raw::c_int; + +pub struct EmitBuilder { + object: *mut RcImpl, + frame: Frame, +} + +impl EmitBuilder { + pub fn build(frame: Frame) -> V8Handler { + V8Handler::new(Self { + object: core::ptr::null_mut(), + frame, + }) + } +} + +impl Rc for EmitBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapV8Handler for EmitBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for EmitBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + frame: self.frame.clone(), + } + } +} + +impl ImplV8Handler for EmitBuilder { + fn execute( + &self, + _: Option<&CefString>, + _: Option<&mut V8Value>, + arguments: Option<&[Option]>, + _: Option<&mut Option>, + _: Option<&mut CefString>, + ) -> c_int { + if let Some(mut process) = process_message_create(Some(&PROCESS_MESSAGE_JS_EMIT.into())) + && let Some(arguments_list) = process.argument_list() + && let Some(arguments) = arguments + && let Some(Some(arg)) = arguments.first() + && let Some(arg) = v8_value_to_json(arg) + && let Ok(arg) = serde_json::to_string(&arg) + { + arguments_list.set_string(0, Some(&arg.as_str().into())); + self.frame.send_process_message( + ProcessId::from(cef_process_id_t::PID_BROWSER), + Some(&mut process), + ); + } + 1 + } + + fn get_raw(&self) -> *mut sys::_cef_v8_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/render_process/listen.rs b/crates/bevy_cef_core/src/render_process/listen.rs new file mode 100644 index 0000000..d744478 --- /dev/null +++ b/crates/bevy_cef_core/src/render_process/listen.rs @@ -0,0 +1,77 @@ +use crate::prelude::LISTEN_EVENTS; +use crate::util::IntoString; +use cef::rc::{Rc, RcImpl}; +use cef::{CefString, Frame, ImplV8Handler, ImplV8Value, V8Handler, V8Value, WrapV8Handler, sys}; +use std::os::raw::c_int; + +pub struct ListenBuilder { + object: *mut RcImpl, + frame: Frame, +} + +impl ListenBuilder { + pub fn build(frame: Frame) -> V8Handler { + V8Handler::new(Self { + object: core::ptr::null_mut(), + frame, + }) + } +} + +impl Rc for ListenBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapV8Handler for ListenBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for ListenBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { + object, + frame: self.frame.clone(), + } + } +} + +impl ImplV8Handler for ListenBuilder { + fn execute( + &self, + _: Option<&CefString>, + _: Option<&mut V8Value>, + arguments: Option<&[Option]>, + _: Option<&mut Option>, + _: Option<&mut CefString>, + ) -> c_int { + if let Some(arguments) = arguments + && let Some(Some(id)) = arguments.first() + && 0 < id.is_string() + && let Some(Some(callback)) = arguments.get(1) + && 0 < callback.is_function() + { + LISTEN_EVENTS + .lock() + .unwrap() + .insert(id.string_value().into_string(), callback.clone()); + } + 1 + } + + #[inline] + fn get_raw(&self) -> *mut sys::_cef_v8_handler_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/render_process/render_process_handler.rs b/crates/bevy_cef_core/src/render_process/render_process_handler.rs new file mode 100644 index 0000000..2ed4cb4 --- /dev/null +++ b/crates/bevy_cef_core/src/render_process/render_process_handler.rs @@ -0,0 +1,191 @@ +use crate::prelude::{EmitBuilder, IntoString}; +use crate::render_process::brp::BrpBuilder; +use crate::render_process::listen::ListenBuilder; +use crate::util::json_to_v8; +use crate::util::v8_accessor::V8DefaultAccessorBuilder; +use crate::util::v8_interceptor::V8DefaultInterceptorBuilder; +use bevy::platform::collections::HashMap; +use bevy_remote::BrpResult; +use cef::rc::{Rc, RcImpl}; +use cef::{ + Browser, Frame, ImplFrame, ImplListValue, ImplProcessMessage, ImplRenderProcessHandler, + ImplV8Context, ImplV8Value, ProcessId, ProcessMessage, V8Context, V8Propertyattribute, V8Value, + WrapRenderProcessHandler, sys, v8_value_create_function, v8_value_create_object, +}; +use std::os::raw::c_int; +use std::sync::Mutex; + +pub(crate) static BRP_PROMISES: Mutex> = Mutex::new(HashMap::new()); +pub(crate) static LISTEN_EVENTS: Mutex> = Mutex::new(HashMap::new()); + +pub const PROCESS_MESSAGE_BRP: &str = "brp"; +pub const PROCESS_MESSAGE_HOST_EMIT: &str = "host-emit"; +pub const PROCESS_MESSAGE_JS_EMIT: &str = "js-emit"; + +pub struct RenderProcessHandlerBuilder { + object: *mut RcImpl, +} + +impl RenderProcessHandlerBuilder { + pub fn build() -> RenderProcessHandlerBuilder { + RenderProcessHandlerBuilder { + object: core::ptr::null_mut(), + } + } +} + +impl WrapRenderProcessHandler for RenderProcessHandlerBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Rc for RenderProcessHandlerBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for RenderProcessHandlerBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { object } + } +} + +impl ImplRenderProcessHandler for RenderProcessHandlerBuilder { + fn on_context_created( + &self, + _browser: Option<&mut Browser>, + frame: Option<&mut Frame>, + context: Option<&mut V8Context>, + ) { + if let Some(g) = context.and_then(|c| c.global()) + && let Some(frame) = frame + && let Some(mut cef) = v8_value_create_object( + Some(&mut V8DefaultAccessorBuilder::build()), + Some(&mut V8DefaultInterceptorBuilder::build()), + ) + && let Some(mut brp) = v8_value_create_function( + Some(&"brp".into()), + Some(&mut BrpBuilder::build(frame.clone())), + ) + && let Some(mut emit) = v8_value_create_function( + Some(&"emit".into()), + Some(&mut EmitBuilder::build(frame.clone())), + ) + && let Some(mut listen) = v8_value_create_function( + Some(&"listen".into()), + Some(&mut ListenBuilder::build(frame.clone())), + ) + { + cef.set_value_bykey( + Some(&"brp".into()), + Some(&mut brp), + V8Propertyattribute::default(), + ); + cef.set_value_bykey( + Some(&"emit".into()), + Some(&mut emit), + V8Propertyattribute::default(), + ); + cef.set_value_bykey( + Some(&"listen".into()), + Some(&mut listen), + V8Propertyattribute::default(), + ); + g.set_value_bykey( + Some(&"cef".into()), + Some(&mut cef), + V8Propertyattribute::default(), + ); + }; + } + + fn on_process_message_received( + &self, + _browser: Option<&mut Browser>, + frame: Option<&mut Frame>, + _: ProcessId, + message: Option<&mut ProcessMessage>, + ) -> c_int { + if let Some(message) = message + && let Some(frame) = frame + && let Some(ctx) = frame.v8_context() + { + match message.name().into_string().as_str() { + PROCESS_MESSAGE_BRP => { + handle_brp_message(message, ctx); + } + PROCESS_MESSAGE_HOST_EMIT => { + handle_listen_message(message, ctx); + } + _ => {} + } + }; + 1 + } + + #[inline] + fn get_raw(&self) -> *mut sys::_cef_render_process_handler_t { + self.object.cast() + } +} + +fn handle_brp_message(message: &ProcessMessage, ctx: V8Context) { + let Some(argument_list) = message.argument_list() else { + return; + }; + let id = argument_list.string(0).into_string(); + let payload = argument_list.string(1).into_string(); + let Ok(Some(promise)) = BRP_PROMISES.lock().map(|mut p| p.remove(&id)) else { + return; + }; + + if let Ok(brp_result) = serde_json::from_str::(&payload) { + ctx.enter(); + match brp_result { + Ok(v) => { + promise.resolve_promise(json_to_v8(v).as_mut()); + } + Err(e) => { + promise.reject_promise(Some(&e.message.as_str().into())); + } + } + ctx.exit(); + } +} + +fn handle_listen_message(message: &ProcessMessage, mut ctx: V8Context) { + let Some(argument_list) = message.argument_list() else { + return; + }; + let id = argument_list.string(0).into_string(); + let payload = argument_list.string(1).into_string(); + + ctx.enter(); + if let Ok(value) = serde_json::from_str::(&payload) + && let Ok(events) = LISTEN_EVENTS.lock() + { + let mut obj = v8_value_create_object( + Some(&mut V8DefaultAccessorBuilder::build()), + Some(&mut V8DefaultInterceptorBuilder::build()), + ); + let Some(callback) = events.get(&id) else { + return; + }; + callback.execute_function_with_context( + Some(&mut ctx), + obj.as_mut(), + Some(&[json_to_v8(value)]), + ); + } + ctx.exit(); +} diff --git a/crates/bevy_cef_core/src/util.rs b/crates/bevy_cef_core/src/util.rs new file mode 100644 index 0000000..277262c --- /dev/null +++ b/crates/bevy_cef_core/src/util.rs @@ -0,0 +1,140 @@ +pub mod v8_accessor; +mod v8_handler_wrapper; +pub mod v8_interceptor; + +use crate::util::v8_accessor::V8DefaultAccessorBuilder; +use crate::util::v8_interceptor::V8DefaultInterceptorBuilder; +use cef::rc::ConvertParam; +use cef::{ + CefStringList, CefStringUserfreeUtf16, CefStringUtf16, ImplV8Value, V8Propertyattribute, + v8_value_create_array, v8_value_create_bool, v8_value_create_double, v8_value_create_int, + v8_value_create_null, v8_value_create_object, v8_value_create_string, +}; +use cef_dll_sys::_cef_string_utf16_t; +use cef_dll_sys::cef_scheme_options_t::{ + CEF_SCHEME_OPTION_CORS_ENABLED, CEF_SCHEME_OPTION_LOCAL, CEF_SCHEME_OPTION_SECURE, + CEF_SCHEME_OPTION_STANDARD, +}; +use std::env::home_dir; +use std::path::PathBuf; + +pub const SCHEME_CEF: &str = "cef"; + +pub const HOST_CEF: &str = "localhost"; + +pub fn cef_scheme_flags() -> u32 { + CEF_SCHEME_OPTION_STANDARD as u32 + | CEF_SCHEME_OPTION_SECURE as u32 + | CEF_SCHEME_OPTION_LOCAL as u32 + | CEF_SCHEME_OPTION_CORS_ENABLED as u32 +} + +pub fn debug_chromium_libraries_path() -> PathBuf { + debug_chromium_embedded_framework_dir_path().join("Libraries") +} + +pub fn debug_chromium_embedded_framework_dir_path() -> PathBuf { + debug_cef_path().join("Chromium Embedded Framework.framework") +} + +pub fn debug_cef_path() -> PathBuf { + home_dir().unwrap().join(".local").join("share").join("cef") +} + +pub fn debug_render_process_path() -> PathBuf { + cargo_bin_path().join("bevy_cef_debug_render_process") +} + +pub fn cargo_bin_path() -> PathBuf { + home_dir().unwrap().join(".cargo").join("bin") +} + +pub trait IntoString { + fn into_string(self) -> String; +} + +impl IntoString for CefStringUserfreeUtf16 { + fn into_string(self) -> String { + let ptr: *mut _cef_string_utf16_t = self.into_raw(); + CefStringUtf16::from(ptr).to_string() + } +} + +pub fn v8_value_to_json(v8: &cef::V8Value) -> Option { + if v8.is_bool().is_positive() { + Some(serde_json::Value::Bool(v8.bool_value().is_positive())) + } else if v8.is_int().is_positive() { + Some(serde_json::Value::Number(serde_json::Number::from( + v8.int_value(), + ))) + } else if v8.is_double().is_positive() { + Some(serde_json::Value::Number( + serde_json::Number::from_f64(v8.double_value()).unwrap(), + )) + } else if v8.is_string().is_positive() { + Some(serde_json::Value::String(v8.string_value().into_string())) + } else if v8.is_null().is_positive() || v8.is_undefined().is_positive() { + Some(serde_json::Value::Null) + } else if v8.is_array().is_positive() { + let mut array = Vec::new(); + let mut keys = CefStringList::new(); + v8.keys(Some(&mut keys)); + for key in keys.into_iter() { + if let Some(v) = v8.value_bykey(Some(&key.as_str().into())) + && let Some(serialized) = v8_value_to_json(&v) + { + { + array.push(serialized); + } + } + } + Some(serde_json::Value::Array(array)) + } else if v8.is_object().is_positive() { + let mut object = serde_json::Map::new(); + let mut keys = CefStringList::new(); + v8.keys(Some(&mut keys)); + for key in keys.into_iter() { + if let Some(v) = v8.value_bykey(Some(&key.as_str().into())) + && let Some(serialized) = v8_value_to_json(&v) + { + { + object.insert(key, serialized); + } + } + } + Some(serde_json::Value::Object(object)) + } else { + None + } +} + +pub fn json_to_v8(v: serde_json::Value) -> Option { + match v { + serde_json::Value::Null => v8_value_create_null(), + serde_json::Value::Bool(b) => v8_value_create_bool(b as _), + serde_json::Value::Number(n) if n.is_i64() => v8_value_create_int(n.as_i64()? as i32), + serde_json::Value::Number(n) => v8_value_create_double(n.as_f64()?), + serde_json::Value::String(s) => v8_value_create_string(Some(&s.as_str().into())), + serde_json::Value::Array(arr) => { + let v8_array = v8_value_create_array(arr.len() as _)?; + for (i, item) in arr.into_iter().enumerate() { + v8_array.set_value_byindex(i as _, json_to_v8(item).as_mut()); + } + Some(v8_array) + } + serde_json::Value::Object(obj) => { + let v8_object = v8_value_create_object( + Some(&mut V8DefaultAccessorBuilder::build()), + Some(&mut V8DefaultInterceptorBuilder::build()), + )?; + for (key, value) in obj { + v8_object.set_value_bykey( + Some(&key.as_str().into()), + json_to_v8(value).as_mut(), + V8Propertyattribute::default(), + ); + } + Some(v8_object) + } + } +} diff --git a/crates/bevy_cef_core/src/util/v8_accessor.rs b/crates/bevy_cef_core/src/util/v8_accessor.rs new file mode 100644 index 0000000..258b764 --- /dev/null +++ b/crates/bevy_cef_core/src/util/v8_accessor.rs @@ -0,0 +1,48 @@ +use cef::rc::{Rc, RcImpl}; +use cef::{ImplV8Accessor, V8Accessor, WrapV8Accessor, sys}; +use cef_dll_sys::_cef_v8_accessor_t; + +pub struct V8DefaultAccessorBuilder { + object: *mut RcImpl<_cef_v8_accessor_t, Self>, +} + +impl V8DefaultAccessorBuilder { + pub fn build() -> V8Accessor { + V8Accessor::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl Rc for V8DefaultAccessorBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for V8DefaultAccessorBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { object } + } +} + +impl WrapV8Accessor for V8DefaultAccessorBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_v8_accessor_t, Self>) { + self.object = object; + } +} + +impl ImplV8Accessor for V8DefaultAccessorBuilder { + #[inline] + fn get_raw(&self) -> *mut _cef_v8_accessor_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_core/src/util/v8_handler_wrapper.rs b/crates/bevy_cef_core/src/util/v8_handler_wrapper.rs new file mode 100644 index 0000000..2f59244 --- /dev/null +++ b/crates/bevy_cef_core/src/util/v8_handler_wrapper.rs @@ -0,0 +1,70 @@ +// use std::os::raw::c_int; +// use bevy::ecs::reflect::ReflectCommandExt; +// use cef::rc::{Rc, RcImpl}; +// use cef::{sys, CefString, ImplV8Handler, V8Handler, V8Value, WrapV8Handler}; +// use cef_dll_sys::{_cef_v8_handler_t, _cef_v8_value_t, cef_string_t}; +// +// pub struct V8HandlerWrapBuilder { +// base: T, +// } +// +// impl V8HandlerWrapBuilder { +// pub fn build(base: T) -> V8Handler { +// V8Handler::new(Self { +// base, +// }) +// } +// } +// +// impl Rc for V8HandlerWrapBuilder { +// fn as_base(&self) -> &sys::cef_base_ref_counted_t { +// self.base.as_base() +// } +// } +// +// impl ImplV8Handler for V8HandlerWrapBuilder { +// fn execute(&self, name: Option<&CefString>, object: Option<&mut V8Value>, arguments: Option<&[Option]>, retval: Option<&mut Option>, exception: Option<&mut CefString>) -> c_int { +// self.base.execute( +// name, +// object, +// arguments, +// retval, +// exception, +// ) +// } +// +// fn get_raw(&self) -> *mut _cef_v8_handler_t { +// self.base.get_raw() +// } +// +// fn init_methods(object: &mut _cef_v8_handler_t) { +// T::init_methods(object); +// +// } +// } +// +// impl Clone for V8HandlerWrapBuilder { +// fn clone(&self) -> Self { +// Self{ +// base: self.base.clone(), +// } +// } +// } +// +// impl cef::WrapV8Handler for V8HandlerWrapBuilder { +// fn wrap_rc(&mut self, object: *mut RcImpl) { +// self.base.wrap_rc(object); +// } +// } +// +// // extern "C" fn execute( +// // self_: *mut _cef_v8_handler_t, +// // name: *const cef_string_t, +// // object: *mut _cef_v8_value_t, +// // arguments_count: usize, +// // arguments: *const *mut _cef_v8_value_t, +// // retval: *mut *mut _cef_v8_value_t, +// // exception: *mut cef_string_t, +// // ) -> ::std::os::raw::c_int { +// // +// // } diff --git a/crates/bevy_cef_core/src/util/v8_interceptor.rs b/crates/bevy_cef_core/src/util/v8_interceptor.rs new file mode 100644 index 0000000..e3ffbb5 --- /dev/null +++ b/crates/bevy_cef_core/src/util/v8_interceptor.rs @@ -0,0 +1,48 @@ +use cef::rc::{Rc, RcImpl}; +use cef::{ImplV8Interceptor, V8Interceptor, WrapV8Interceptor, sys}; +use cef_dll_sys::_cef_v8_interceptor_t; + +pub struct V8DefaultInterceptorBuilder { + object: *mut RcImpl<_cef_v8_interceptor_t, Self>, +} + +impl V8DefaultInterceptorBuilder { + pub fn build() -> V8Interceptor { + V8Interceptor::new(Self { + object: core::ptr::null_mut(), + }) + } +} + +impl WrapV8Interceptor for V8DefaultInterceptorBuilder { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_v8_interceptor_t, Self>) { + self.object = object; + } +} + +impl Rc for V8DefaultInterceptorBuilder { + fn as_base(&self) -> &sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for V8DefaultInterceptorBuilder { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + Self { object } + } +} + +impl ImplV8Interceptor for V8DefaultInterceptorBuilder { + #[inline] + fn get_raw(&self) -> *mut _cef_v8_interceptor_t { + self.object.cast() + } +} diff --git a/crates/bevy_cef_debug_render_process/Cargo.toml b/crates/bevy_cef_debug_render_process/Cargo.toml new file mode 100644 index 0000000..ba64cd7 --- /dev/null +++ b/crates/bevy_cef_debug_render_process/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bevy_cef_debug_render_process" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +cef = { workspace = true } +bevy_cef_core = { workspace = true } +libloading = { version = "0.8"} \ No newline at end of file diff --git a/crates/bevy_cef_debug_render_process/README.md b/crates/bevy_cef_debug_render_process/README.md new file mode 100644 index 0000000..44aa934 --- /dev/null +++ b/crates/bevy_cef_debug_render_process/README.md @@ -0,0 +1,12 @@ +# bevy_cef_debug_render_process + +A debug render process for [bevy_cef](ttps://github.com/not-elm/bevy_cef) + +This application is a development tool to build CEF on macOS without requiring an app bundle, +and if you create a release bundle, should create a separate render process. + +## Install + +```shell +> cargo install bevy_cef_debug_render_process +``` \ No newline at end of file diff --git a/crates/bevy_cef_debug_render_process/src/main.rs b/crates/bevy_cef_debug_render_process/src/main.rs new file mode 100644 index 0000000..b5374f7 --- /dev/null +++ b/crates/bevy_cef_debug_render_process/src/main.rs @@ -0,0 +1,19 @@ +use bevy_cef_core::prelude::{DebugLibraryLoader, RenderProcessAppBuilder}; +use cef::{args::Args, *}; + +fn main() { + let args = Args::new(); + #[cfg(target_os = "macos")] + let _loader = { + let loader = DebugLibraryLoader::new(); + assert!(loader.load()); + loader + }; + let _ = api_hash(sys::CEF_API_VERSION_LAST, 0); + let mut app = RenderProcessAppBuilder::build(); + execute_process( + Some(args.as_main_args()), + Some(&mut app), + std::ptr::null_mut(), + ); +} diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..c45336d --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["not-elm"] +language = "en" +src = "src" +title = "bevy_cef" diff --git a/docs/src/README.md b/docs/src/README.md new file mode 100644 index 0000000..8ce7f5e --- /dev/null +++ b/docs/src/README.md @@ -0,0 +1,91 @@ +# Introduction to bevy_cef + +**bevy_cef** is a powerful Bevy plugin that integrates the Chromium Embedded Framework (CEF) into Bevy applications, enabling you to render web content as 3D objects in your game world or as UI overlays. + +## What is bevy_cef? + +bevy_cef bridges the gap between modern web technologies and Bevy's 3D engine by: + +- **Embedding webviews** as textures on 3D meshes or 2D sprites +- **Supporting bidirectional communication** between JavaScript and Bevy systems +- **Providing a multi-process architecture** for stability and performance +- **Offering local asset serving** through a custom URL scheme +- **Enabling developer tools integration** for debugging web content + +## Key Features + +### 🌐 Web Content Rendering +- Render any web page as a texture on 3D objects +- Support for HTML5, CSS3, and modern JavaScript +- Local file serving via the `cef://localhost/` scheme +- Remote web page loading with full browser compatibility + +### 🔄 Inter-Process Communication (IPC) +- **JS Emit**: Send events from JavaScript to Bevy systems +- **Host Emit**: Send events from Bevy to JavaScript +- **Bevy Remote Protocol (BRP)**: Bidirectional RPC communication + +### 🎮 Interactive Controls +- Keyboard input forwarding to webviews +- Mouse interaction support +- Navigation controls (back, forward, refresh) +- Zoom level management +- Audio muting capabilities + +### 🔧 Developer Experience +- Chrome DevTools integration for debugging +- Hot-reload support for local assets +- Comprehensive error handling and logging +- Extensive customization options + +## Architecture Overview + +bevy_cef uses a multi-process architecture similar to modern web browsers: + +- **Browser Process**: The main Bevy application process +- **Render Process**: Separate CEF process for web content rendering +- **IPC Communication**: Secure inter-process communication channels + +This design ensures stability - if a web page crashes, it won't bring down your entire application. + +## Use Cases + +### Game UI +Create rich, responsive game interfaces using familiar web technologies: +```rust +commands.spawn(( + CefWebviewUri::local("ui/main-menu.html"), + // Render as 2D sprite overlay +)); +``` + +### In-World Displays +Embed interactive web content directly in your 3D world: +```rust +commands.spawn(( + CefWebviewUri::new("https://example.com"), + Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))), + MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())), +)); +``` + +### Data Visualization +Display real-time data using web-based charting libraries: +```rust +// Load a local HTML file with Chart.js or D3.js +commands.spawn(( + CefWebviewUri::local("charts/dashboard.html"), + WebviewSize(Vec2::new(1920.0, 1080.0)), +)); +``` + +### Development Tools +Integrate web-based development and debugging interfaces directly into your game editor or development build. + +## Getting Started + +Ready to integrate web content into your Bevy application? Check out the [Quick Start](quick-start.md) guide to get up and running in minutes, or dive into [Basic Concepts](basic-concepts.md) to understand the fundamental components and systems. + +## Platform Support + +Currently, bevy_cef focuses on macOS development with plans for expanded platform support. The plugin automatically handles CEF framework installation and configuration on supported platforms. \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..07f48ef --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,12 @@ +# Summary + +[Introduction](README.md) + +# Getting Started +- [Installation](installation.md) +- [Quick Start](quick-start.md) +- [Basic Concepts](basic-concepts.md) + +# User Guide +- [Core Components](core-components.md) + \ No newline at end of file diff --git a/docs/src/basic-concepts.md b/docs/src/basic-concepts.md new file mode 100644 index 0000000..0f0e194 --- /dev/null +++ b/docs/src/basic-concepts.md @@ -0,0 +1,274 @@ +# Basic Concepts + +Understanding these fundamental concepts will help you make the most of bevy_cef in your projects. + +## Multi-Process Architecture + +bevy_cef follows a multi-process architecture similar to modern web browsers: + +### Browser Process +- **Main Application**: Your Bevy game/app runs here +- **CEF Management**: Handles browser creation and management +- **IPC Coordination**: Manages communication with render processes + +### Render Process +- **Web Content**: Each webview runs in a separate process +- **Isolation**: Crashes in web content don't affect your main application +- **Sandboxing**: Enhanced security through process separation + +### Benefits +- **Stability**: Web content crashes won't crash your game +- **Performance**: CPU-intensive web content won't block your game loop +- **Security**: Sandboxed execution of untrusted web content + +## Core Components + +bevy_cef provides several key components that work together: + +### CefWebviewUri +The primary component that defines what web content to display: + +```rust +// Remote web page +CefWebviewUri::new("https://example.com") + +// Local HTML file from assets/ +CefWebviewUri::local("ui/menu.html") + +// Equivalent to above +CefWebviewUri::new("cef://localhost/ui/menu.html") +``` + +### WebviewSize +Controls the rendering resolution of the webview (not the 3D object size): + +```rust +WebviewSize(Vec2::new(1920.0, 1080.0)) // High resolution +WebviewSize(Vec2::new(800.0, 600.0)) // Standard resolution +WebviewSize::default() // 800x800 pixels +``` + +### Material Integration +Webviews integrate with Bevy's material system: + +```rust +// Standard material with webview texture +WebviewExtendStandardMaterial::default() + +// Custom material properties +WebviewExtendStandardMaterial { + base: StandardMaterial { + unlit: true, + emissive: Color::WHITE.into(), + ..default() + }, + ..default() +} +``` + +## Component Requirements + +When you add a `CefWebviewUri` component, bevy_cef automatically requires several other components: + +```rust +#[require(WebviewSize, ZoomLevel, AudioMuted)] +pub struct CefWebviewUri(pub String); +``` + +This means every webview entity automatically gets: +- **WebviewSize**: Default 800x800 resolution +- **ZoomLevel**: Default zoom (0.0 = browser default) +- **AudioMuted**: Default unmuted (false) + +## Rendering Modes + +bevy_cef supports different rendering approaches: + +### 3D Mesh Rendering +Render web content on 3D objects in your world: + +```rust +commands.spawn(( + CefWebviewUri::local("interface.html"), + Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))), + MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())), +)); +``` + +### 2D Sprite Rendering +Render web content as UI overlays: + +```rust +commands.spawn(( + CefWebviewUri::local("hud.html"), + Sprite::default(), + WebviewSpriteMaterial::default(), +)); +``` + +## Local Asset Serving + +bevy_cef includes a built-in web server that serves files from your `assets/` directory: + +### Custom Scheme +- **Scheme**: `cef://localhost/` +- **Path Mapping**: `assets/` directory maps to the root +- **Example**: `assets/ui/menu.html` → `cef://localhost/ui/menu.html` + +### Supported File Types +- HTML, CSS, JavaScript +- Images (PNG, JPG, SVG, etc.) +- Fonts (WOFF, TTF, etc.) +- JSON, XML, and other data files + +### Asset Organization +``` +assets/ +├── ui/ +│ ├── menu.html +│ ├── styles.css +│ └── script.js +├── images/ +│ └── logo.png +└── data/ + └── config.json +``` + +## Inter-Process Communication (IPC) + +bevy_cef provides three communication patterns: + +### 1. JavaScript to Bevy (JS Emit) +Send events from web content to Bevy systems: + +```javascript +// In your HTML/JavaScript +window.cef.emit('player_action', { action: 'jump', power: 10 }); +``` + +```rust +fn main(){ + App::new() + .add_plugins(JsEmitEventPlugin::::default()) + // ... +} + +// In your Bevy system +#[derive(Event, Deserialize)] +struct PlayerAction { + action: String, + power: i32, +} + +fn handle_player_action(trigger: Trigger) { + let action = trigger.event(); + info!("Player action: {} with power {}", action.action, action.power); +} +``` + +### 2. Bevy to JavaScript (Host Emit) +Send events from Bevy to web content: + +```rust +// In your Bevy system +commands.entity(webview_entity).trigger(HostEmitEvent { + event_name: "score_update".to_string(), + data: json!({ "score": 1000, "level": 3 }), +}); +``` + +```javascript +// In your HTML/JavaScript +window.cef.listen('score_update', (data) => { + document.getElementById('score').textContent = data.score; + document.getElementById('level').textContent = data.level; +}); +``` + +### 3. Bevy Remote Protocol (BRP) + +Please see [here](https://gist.github.com/coreh/1baf6f255d7e86e4be29874d00137d1d) for about the Bevy Remote Protocol (BRP). + +Bidirectional RPC calls for complex interactions: + +```rust +// Register BRP method in Bevy +app.add_plugins(RemotePlugin::default().with_method("get_player_stats", get_stats)); +``` + +```javascript +// Call from JavaScript +const stats = await window.cef.brp({ + method: 'get_player_stats', + params: { playerId: 42 } +}); +``` + +## User Interaction + +### Input Handling +Webviews automatically receive keyboard and mouse input when focused: +- **Keyboard**: All keyboard events are forwarded +- **Mouse**: Click, scroll, and hover events work naturally +- **Focus**: Multiple webviews can coexist; input goes to the focused one + +### Navigation +Control web navigation programmatically: + +```rust +commands.entity(webview).trigger(RequestGoBack); +commands.entity(webview).trigger(ReqeustGoForward); +``` + +### Zoom Control +Manage zoom levels per webview: + +```rust +// Set zoom level (0.0 = default, positive = zoom in, negative = zoom out) +commands.entity(webview).insert(ZoomLevel(1.2)); + +// Reset to default zoom +commands.entity(webview).insert(ZoomLevel(0.0)); +``` + +## Developer Experience + +### Developer Tools +Access Chrome DevTools for debugging: + +```rust +// Show developer tools for a webview +commands.entity(webview).trigger(RequestShowDevTool); + +// Close developer tools +commands.entity(webview).trigger(RequestCloseDevtool); +``` + +### Hot Reload +Local assets automatically reload when changed during development. + +## Best Practices + +### Component Organization +```rust +// Good: Group related components +commands.spawn(( + // The uri convert to `cef://localhost/index.html` + CefWebviewUri::local("index.html"), + WebviewSize(Vec2::new(1920.0, 200.0)), + ZoomLevel(0.8), + AudioMuted(true), + Transform::from_translation(Vec3::new(0.0, 5.0, 0.0)), + Mesh3d(meshes.add(Quad::new(Vec2::new(4.0, 1.0)))), + MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())), +)); +``` + +## Next Steps + +Now that you understand the basic concepts: + +- Explore [Core Components](core-components.md) in detail +- Learn about [Webview Rendering](webview-rendering.md) techniques +- Dive into [Inter-Process Communication](ipc.md) patterns +- Check out practical [Examples](examples/simple.md) \ No newline at end of file diff --git a/docs/src/core-components.md b/docs/src/core-components.md new file mode 100644 index 0000000..d3649d4 --- /dev/null +++ b/docs/src/core-components.md @@ -0,0 +1,407 @@ +# Core Components + +bevy_cef provides several essential components that control webview behavior. Understanding these components is crucial +for effective use of the library. + +## Component Overview + +| Component | Purpose | Default Value | Required | +|-----------------|-------------------------------|-----------------|--------------| +| `CefWebviewUri` | Specifies web content URL | None | ✅ Primary | +| `WebviewSize` | Controls rendering resolution | 800×800 | ✅ Auto-added | +| `ZoomLevel` | Controls webview zoom | 0.0 (default) | ✅ Auto-added | +| `AudioMuted` | Controls audio output | false (unmuted) | ✅ Auto-added | +| `HostWindow` | Parent window specification | Primary window | ❌ Optional | + +## CefWebviewUri + +The primary component that defines what web content to display. + +### Usage + +```rust +use bevy_cef::prelude::*; + +// Remote web page +let webview = CefWebviewUri::new("https://example.com"); + +// Local HTML file from assets/ directory +let webview = CefWebviewUri::local("ui/menu.html"); + +// Equivalent to local() method +let webview = CefWebviewUri::new("cef://localhost/ui/menu.html"); +``` + +### Implementation Details + +```rust +#[derive(Component, Debug, Clone, PartialEq, Eq, Hash, Reflect)] +#[require(WebviewSize, ZoomLevel, AudioMuted)] +pub struct CefWebviewUri(pub String); +``` + +The `#[require(...)]` attribute ensures that every webview automatically gets the essential supporting components. + +### Methods + +- **`new(uri)`**: Create with any valid URL (http, https, cef://localhost/) +- **`local(path)`**: Create with local file path, automatically prefixed with `cef://localhost/` + +### Local File Serving + +When using local files, bevy_cef serves them through a custom scheme: + +- **Scheme**: `cef://localhost/` +- **Root Directory**: Your project's `assets/` folder +- **Path Resolution**: Relative paths from assets/ root + +**Example File Structure:** + +``` +assets/ +├── index.html → cef://localhost/index.html +├── ui/ +│ ├── menu.html → cef://localhost/ui/menu.html +│ └── styles.css → cef://localhost/ui/styles.css +└── js/ + └── app.js → cef://localhost/js/app.js +``` + +## WebviewSize + +Controls the internal rendering resolution of the webview, independent of the 3D object size. + +### Usage + +```rust +use bevy::math::Vec2; + +// High resolution webview +WebviewSize(Vec2::new(1920.0, 1080.0)) + +// Standard resolution +WebviewSize(Vec2::new(800.0, 600.0)) + +// Square webview +WebviewSize(Vec2::splat(512.0)) + +// Default size +WebviewSize::default () // 800×800 +``` + +### Performance Considerations + +- **Higher Resolution**: Better quality, more memory usage +- **Lower Resolution**: Better performance, potential pixelation +- **Aspect Ratio**: Match your 3D mesh proportions for best results + +```rust +// Example: Widescreen webview for cinematic content +commands.spawn(( +CefWebviewUri::local("video-player.html"), +WebviewSize(Vec2::new(1920.0, 800.0)), // 21:9 aspect ratio +Mesh3d(meshes.add(Quad::new(Vec2::new(4.8, 2.0)))), // Match aspect in 3D +MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default ())), +)); +``` + +### Dynamic Resizing + +You can change webview size at runtime: + +```rust +fn resize_webview( + mut webviews: Query<&mut WebviewSize>, + keyboard: Res>, +) { + if keyboard.just_pressed(KeyCode::KeyR) { + for mut size in webviews.iter_mut() { + size.0 *= 1.5; // Increase resolution by 50% + } + } +} +``` + +## ZoomLevel + +Controls the zoom level of web content within the webview. + +### Usage + +```rust +// Default zoom (browser default) +ZoomLevel(0.0) + +// Zoom in 20% +ZoomLevel(1.2) + +// Zoom out 20% +ZoomLevel(0.8) + +// Maximum zoom in +ZoomLevel(3.0) + +// Maximum zoom out +ZoomLevel(0.25) +``` + +### Zoom Behavior + +- **0.0**: Browser default zoom level +- **Positive values**: Zoom in (1.2 = 120% of default) +- **Negative values**: Zoom out (0.8 = 80% of default) +- **Range**: Typically 0.25 to 3.0 (25% to 300%) + +### Dynamic Zoom Control + +```rust +fn zoom_control( + mut webviews: Query<&mut ZoomLevel>, + keyboard: Res>, +) { + for mut zoom in webviews.iter_mut() { + if keyboard.just_pressed(KeyCode::Equal) { + zoom.0 = (zoom.0 + 0.1).min(3.0); // Zoom in + } + if keyboard.just_pressed(KeyCode::Minus) { + zoom.0 = (zoom.0 - 0.1).max(0.25); // Zoom out + } + if keyboard.just_pressed(KeyCode::Digit0) { + zoom.0 = 0.0; // Reset zoom + } + } +} +``` + +### Use Cases + +- **Accessibility**: Larger text for readability +- **Dense Content**: Fit more information in limited space +- **Responsive Design**: Adapt to different screen sizes +- **User Preference**: Allow users to adjust comfortable viewing size + +## AudioMuted + +Controls whether audio from the webview is muted. + +### Usage + +```rust +// Audio enabled (default) +AudioMuted(false) + +// Audio muted +AudioMuted(true) +``` + +### Dynamic Audio Control + +```rust +fn toggle_audio( + mut webviews: Query<&mut AudioMuted>, + keyboard: Res>, +) { + if keyboard.just_pressed(KeyCode::KeyM) { + for mut muted in webviews.iter_mut() { + muted.0 = !muted.0; // Toggle mute state + } + } +} +``` + +### Use Cases + +- **Background Content**: Mute decorative webviews +- **Multiple Webviews**: Prevent audio conflicts +- **User Control**: Provide mute/unmute functionality +- **Game State**: Mute during pause or cutscenes + +## HostWindow (Optional) + +Specifies which Bevy window should be the parent of the webview. If not provided, the primary window is used. + +### Usage + +```rust +// Use primary window (default behavior) +commands.spawn(( +CefWebviewUri::local("ui.html"), +// No HostWindow component needed +)); + +// Specify a particular window +commands.spawn(( +CefWebviewUri::local("ui.html"), +HostWindow(secondary_window_entity), +)); +``` + +### Multi-Window Applications + +```rust +fn setup_multi_window( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // Create secondary window + let secondary_window = commands.spawn(Window { + title: "Secondary Display".to_string(), + resolution: (800.0, 600.0).into(), + ..default() + }).id(); + + // Main webview in primary window + commands.spawn(( + CefWebviewUri::local("main-ui.html"), + Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))), + MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())), + )); + + // Secondary webview in secondary window + commands.spawn(( + CefWebviewUri::local("secondary-ui.html"), + HostWindow(secondary_window), + Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))), + MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())), + )); +} +``` + +## Component Combinations + +### Common Patterns + +**High-Resolution Interactive Display:** + +```rust +commands.spawn(( +CefWebviewUri::local("dashboard.html"), +WebviewSize(Vec2::new(2560.0, 1440.0)), +ZoomLevel(0.0), +AudioMuted(false), +)); +``` + +**Compact Information Panel:** + +```rust +commands.spawn(( +CefWebviewUri::local("info-panel.html"), +WebviewSize(Vec2::new(400.0, 300.0)), +ZoomLevel(0.8), +AudioMuted(true), +)); +``` + +**Video Player:** + +```rust +commands.spawn(( +CefWebviewUri::new("https://player.example.com"), +WebviewSize(Vec2::new(1920.0, 1080.0)), +ZoomLevel(0.0), +AudioMuted(false), // Keep audio for video +)); +``` + +**Background Decoration:** + +```rust +commands.spawn(( +CefWebviewUri::local("animated-bg.html"), +WebviewSize(Vec2::new(1024.0, 1024.0)), +ZoomLevel(0.0), +AudioMuted(true), // No audio for decoration +)); +``` + +## Component Lifecycle + +### Automatic Requirements + +When you add `CefWebviewUri`, the required components are automatically added with default values: + +```rust +// You only need to specify this: +commands.spawn(CefWebviewUri::local("page.html")); + +// But the entity automatically gets: +// - WebviewSize(Vec2::splat(800.0)) +// - ZoomLevel(0.0) +// - AudioMuted(false) +``` + +### Manual Override + +You can override the defaults by adding components explicitly: + +```rust +commands.spawn(( +CefWebviewUri::local("page.html"), +WebviewSize(Vec2::new(1024.0, 768.0)), // Override default +ZoomLevel(1.2), // Override default +AudioMuted(true), // Override default +)); +``` + +### Runtime Modification + +All components can be modified at runtime through standard Bevy systems: + +```rust +fn modify_webview_properties( + mut query: Query<(&mut WebviewSize, &mut ZoomLevel, &mut AudioMuted)>, + time: Res