diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..63c5097e --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,9 @@ +[alias] +xtask = "run --package xtask --" + +[env] +# needed for glsl-to-spirv +CMAKE_POLICY_VERSION_MINIMUM = "3.5" + +[target.wasm32-unknown-unknown] +rustflags = ["--cfg=web_sys_unstable_apis"] diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e1d68b82..12e6d921 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -6,6 +6,8 @@ jobs: fmt-clippy-check-test: name: Format + clippy + check + test runs-on: ubuntu-latest + env: + RUSTFLAGS: "-C link-arg=-Wl,--threads=1" steps: - name: Checkout sources uses: actions/checkout@v2 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9edbec02..1848c270 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,11 +12,11 @@ jobs: steps: - uses: actions/stale@v5 with: - days-before-issue-stale: 60 - days-before-issue-close: 14 + days-before-issue-stale: 1095 + days-before-issue-close: 30 stale-issue-label: "stale" - stale-issue-message: "This issue is stale because it has been open for 60 days with no activity." - close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale. Do not hesitate to open a new issue if you need it." + stale-issue-message: "This issue is stale because it has been open for 3 year with no activity." + close-issue-message: "This issue was closed because it has been inactive for 30 days since being marked as stale. Do not hesitate to open a new issue if you need it." days-before-pr-stale: -1 days-before-pr-close: -1 - repo-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index cdf65fe5..515b8d69 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,11 @@ pkg # allow static images !**/docs/examples/images/**/*.jpg + +# discard test examples +examples/test_*.rs + +.DS_Store + .nix-cargo .direnv diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d130233..5ad65da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,97 @@ # Changelog All notable changes to this project will be documented in this file. +## v0.14.0 - 27/12/2025 + +- Updated `winit` to `0.30`. +- Fixed build issues on Windows and macOS. +- Fixed compatibility with CMake 4.0+. +- Propagated the `serde` feature to `notan_egui`. +- Set `shaderc` as default shader transpiler. + +## v0.13.0 - 29/03/2025 + +- Updated EGUI up to `0.31`. +- Fixed EGUI input scaling when the browser is zoomed in/out. +- Added support for `GL_POINTS` and `gl_PointSize` (point rendering). +- Renamed the point size flags (`point_size` -> `point_size_enabled` -> `point_size_available`) and enabled it by default. +- Added `Rgb24` texture format. +- Added `Rect::contains`. +- Limited `InnerBuffer::global_ubo` size to avoid blowing up the UBO. +- WebGL power preferences now try to pick the best GPU available. +- Removed `Send + Sync` from plugins (they’re not sent across threads anyway). +- Fixed a text transform order issue. +- Lots of dependency updates, formatting and clippy fixes. + +## v0.12.1 - 08/06/2024 + +- Updated EGUI to `0.27`. +- The readme has gifs again. +- Added `app.mouse.clean_button_state` to clean the state of a button. +- Added `xtask` to run project's script like building the examples for web. +- Fixed an issue with EGUI that makes text looks blurry. + +## v0.12.0 - 19/02/2024 + +- Updated EGUI to `0.26`. +- Removed `egui::plugin::Output.needs_repaint()`, now is only used internally and not exposed to users. +- Exposed `notan::draw::DrawBuilder` allowing custom builders. +- Exposed `notan::app::AppTimer`. +- Added `draw.point` allowing to draw points. Check `examples/draw_point.rs`. +- Allow to compile the crate without a backend selected. +- Changed `WindowConfig::set_canvas_id` to `WindowConfig::set_app_id` and is not available for wayland too. +- Fixed `app.request_frame()` when using lazy lopps on Window OS. + +## v0.11.0 - 18/10/2023 + +- Added traits `Serialize` and `Deserialize` to `Color` with the feature `serde` enabled. +- Updated EGUI to `0.23`. +- Fixed an error acquiring the GL Context due required samples configuration. + +## v0.10.0 - 11/09/2023 + +- Added `WindowConfig::set_position` to set x/y position before creating the window. +- Changed `Renderer.begin` uses `Option` instead of `Option<&ClearOption>`. +- Changed sizes and positions for Window and Textures from `i32` to `u32`. +- Added `AppTimer::elapsed` to return time since init as `Duration`. +- Changed `AppTimer::time_since_init` to `AppTimer::elapsed_f32`. +- Changed `WindowConfig` setter method to use the prefix `set_`. +- Removed deprecated `Mouse::local_position`. +- Removed deprecated `mat3_screen_to_local`, `mat3_local_to_screen`, `mat3_local_to_local`. +- Updated dependencies to latest versions. +- Enabled compilation with `--no-default-features` excluding shader compilation macros. +- Deserializing `AtlasFrame` uses a default `pivot` if is empty. +- Added `WindowConfig::set_window_icon_data`. +- Added `WindowConfig::set_taskbar_icon_data`. +- Added example `window_icon_from_raw.rs`. +- Changed `glsl_layout` dependency for `crevice`. +- Updated EGUI to `0.22`. +- Fixed `egui` panic when custom font are set. +- Fixed slow scroll speed. +- Fixed `egui needs_repaint` not working right in some situations. +- Fixed the order of the matrix multiplication for `Draw` methods. +- Improved error messages when `WebGL` and `WebGL2` contexts cannot be adquired. +- Fixed `Buffer` to allow reuse `Uniform Buffers` between pipelines. +- Changed some noisy logs from `debug` to `trace`. +- Added `Clone` to `Random`. +- Reset values of `Mouse::wheel_delta` when the user stops scrolling. +- Added `Mouse::is_scrolling`. +- App's state can use now lifetimes, ie: `State<'n>`. +- Added `Clone` to `AssetsList`. +- The `image` crate on `notan_graphics` is only used when `texture_to_file` is enabled. +- Added `WindowBackend::set_cursor_position`, `Event::MouseMotion` and `Mouse::is_moving`. +- Added new example `window_initial_position.rs`. +- Added mipmap and texture wrapping settings to `RenderTextureBuilder`. +- Added new example `texture_params`. +- Added new example `renderer_stencil`. +- Fixed mouse wheel scroll being ignored when moving the mouse at same time +- Added alt mouse wheel scrolling code to example +- Fixed `set_multisamples`. It is no longer being ignored for winit backend +- Fixed blurry text on egui when using on desktop +- Fixed mono channel audio playing in half of time set for the audio length. +- Added `is_focused()` for winit backend +- Added `window_focus` example + ## v0.9.5 - 19/03/2023 - Increased mouse wheel scroll speed on native platforms. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e908be72 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,24 @@ +# Contributing to Notan + +## Linux +> TODO + +## MacOS +> TODO + +## Windows (MSVC) + +To be able to compile the Notan examples you will need to have the following pre-requirements in your system: + +* [CMake](https://cmake.org/download/) +* [Python](https://www.python.org/downloads/) +* [Ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) + +If you want to use the wasm32 target you will need to execute some additional commands for it: +````cmd +rustup target add wasm32-unknown-unknown +cargo install -f wasm-bindgen-cli --version 0.2.87 +cargo install wasm-opt +```` + +After this you will be able to use the PowerShell scripts in the `scripts` folder to compile the examples or the doc. diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index fe48754c..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,3644 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ab_glyph" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5568a4aa5ba8adf5175c5c460b030e27d8893412976cc37bef0e4fbc16cfbba" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "alsa" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" -dependencies = [ - "alsa-sys", - "bitflags", - "libc", - "nix 0.23.2", -] - -[[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 = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "arboard" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" -dependencies = [ - "clipboard-win", - "log", - "objc", - "objc-foundation", - "objc_id", - "once_cell", - "parking_lot", - "thiserror", - "winapi", - "x11rb", -] - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "atomic_refcell" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "857253367827bd9d0fd973f0ef15506a96e79e41b0ad7aa691203a4e3214f6c8" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "bindgen" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -dependencies = [ - "arrayref", - "byte-tools", -] - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" - -[[package]] -name = "bytemuck" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" - -[[package]] -name = "calloop" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192" -dependencies = [ - "log", - "nix 0.25.1", - "slotmap", - "thiserror", - "vec_map", -] - -[[package]] -name = "cc" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" -dependencies = [ - "jobserver", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "clang-sys" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clipboard-win" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "cmake" -version = "0.1.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" -dependencies = [ - "cc", -] - -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" -dependencies = [ - "bitflags", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "colored" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[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 = "console_log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" -dependencies = [ - "log", - "web-sys", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-text" -version = "19.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "coreaudio-rs" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" -dependencies = [ - "bitflags", - "coreaudio-sys", -] - -[[package]] -name = "coreaudio-sys" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9444b94b8024feecc29e01a9706c69c1e26bfee480221c90764200cfd778fb" -dependencies = [ - "bindgen", -] - -[[package]] -name = "cpal" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f342c1b63e185e9953584ff2199726bf53850d96610a310e3aca09e9405a2d0b" -dependencies = [ - "alsa", - "core-foundation-sys", - "coreaudio-rs", - "jni 0.19.0", - "js-sys", - "libc", - "mach", - "ndk 0.7.0", - "ndk-context", - "oboe", - "once_cell", - "parking_lot", - "stdweb", - "thiserror", - "wasm-bindgen", - "web-sys", - "windows 0.37.0", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.7.1", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossfont" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" -dependencies = [ - "cocoa", - "core-foundation", - "core-foundation-sys", - "core-graphics", - "core-text", - "dwrote", - "foreign-types 0.5.0", - "freetype-rs", - "libc", - "log", - "objc", - "once_cell", - "pkg-config", - "servo-fontconfig", - "winapi", -] - -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" -dependencies = [ - "generic-array", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading", -] - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "serde", - "serde_derive", - "winapi", - "wio", -] - -[[package]] -name = "ecolor" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b601108bca3af7650440ace4ca55b2daf52c36f2635be3587d77b16efd8d0691" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "egui" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a5e883a316e53866977450eecfbcac9c48109c2ab3394af29feb83fcde4ea9" -dependencies = [ - "ahash", - "epaint", - "nohash-hasher", -] - -[[package]] -name = "egui_demo_lib" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b191e5870e6cff885cf56d4e6c3a10ce0c66d9d855a92c1eaca96b0f1111c0aa" -dependencies = [ - "egui", - "egui_extras", - "enum-map", - "tracing", - "unicode_names2", -] - -[[package]] -name = "egui_extras" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1975cd88ff7430f93b29e6b9868b648a8ff6a43b08b9ff8474ee0a648bd8f9a6" -dependencies = [ - "egui", - "serde", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "emath" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277249c8c3430e7127e4f2c40a77485e7baf11ae132ce9b3253a8ed710df0a0" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-map" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c25992259941eb7e57b936157961b217a4fc8597829ddef0596d6c3cd86e1a" -dependencies = [ - "enum-map-derive", - "serde", -] - -[[package]] -name = "enum-map-derive" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "epaint" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de14b65fe5e423e0058f77a8beb2c863b056d0566d6c4ce0d097aa5814cb705a" -dependencies = [ - "ab_glyph", - "ahash", - "atomic_refcell", - "bytemuck", - "ecolor", - "emath", - "nohash-hasher", - "parking_lot", -] - -[[package]] -name = "error-code" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] - -[[package]] -name = "euclid" -version = "0.22.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" -dependencies = [ - "num-traits", -] - -[[package]] -name = "expat-sys" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" -dependencies = [ - "cmake", - "pkg-config", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "fern" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" -dependencies = [ - "colored", - "log", -] - -[[package]] -name = "flate2" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float_next_after" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[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 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "freetype-rs" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" -dependencies = [ - "bitflags", - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - -[[package]] -name = "futures" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" - -[[package]] -name = "futures-executor" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" - -[[package]] -name = "futures-macro" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" - -[[package]] -name = "futures-task" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" - -[[package]] -name = "futures-util" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d00328cedcac5e81c683e5620ca6a30756fc23027ebf9bff405c0e8da1fbb7e" -dependencies = [ - "typenum", -] - -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[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.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" -dependencies = [ - "bytemuck", - "serde", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "glow" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "glsl-layout" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4206fa405b25ca1b607f4db6cfdeeebec5513458d65abe21fc246ee89ac295" -dependencies = [ - "glam", - "glsl-layout-derive", -] - -[[package]] -name = "glsl-layout-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b870f33b0cec52fd0347c09722c2f9a85e9a4e9aa5c0453e75dedc12bde7ddb8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "glsl-to-spirv" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28caebc98746d507603a2d3df66dcbe04e41d4febad0320f3eec1ef72b6bbef1" -dependencies = [ - "cmake", - "sha2", - "tempfile", -] - -[[package]] -name = "glutin" -version = "0.30.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524d807cd49a0c56a53ef9a6738cd15e7c8c4e9d37a3b7fdb3c250c1cd5bf7a3" -dependencies = [ - "bitflags", - "cfg_aliases", - "cgl", - "cocoa", - "core-foundation", - "glutin_egl_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "libloading", - "objc", - "once_cell", - "raw-window-handle 0.5.0", - "wayland-sys 0.30.1", - "windows-sys 0.36.1", - "x11-dl", -] - -[[package]] -name = "glutin-winit" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44949d3a525ad1deae282fc9e7dbf05ddcce98219079c5aea7d9aa6927920c6" -dependencies = [ - "cfg_aliases", - "glutin", - "raw-window-handle 0.5.0", - "winit", -] - -[[package]] -name = "glutin_egl_sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3adbb8fec0e18e340f990c78f79f5f0e142d0d83f46b10909aaa7d251c00afdf" -dependencies = [ - "gl_generator", - "windows-sys 0.36.1", -] - -[[package]] -name = "glutin_glx_sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947c4850c58211c9627969c2b4e2674764b81ae5b47bab2c9a477d7942f96e0f" -dependencies = [ - "gl_generator", - "x11-dl", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c33975a6c9d49d72c8f032a60079bf8df536954fbf9e4cee90396ace815c57" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "glyph_brush" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac02497410cdb5062cc056a33f2e1e19ff69fbf26a4be9a02bf29d6e17ea105b" -dependencies = [ - "glyph_brush_draw_cache", - "glyph_brush_layout", - "log", - "ordered-float", - "rustc-hash", - "twox-hash", -] - -[[package]] -name = "glyph_brush_draw_cache" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6010675390f6889e09a21e2c8b575b3ee25667ea8237a8d59423f73cb8c28610" -dependencies = [ - "ab_glyph", - "crossbeam-channel", - "crossbeam-deque", - "linked-hash-map", - "rayon", - "rustc-hash", -] - -[[package]] -name = "glyph_brush_layout" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38" -dependencies = [ - "ab_glyph", - "approx", - "xi-unicode", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", - "serde", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "image" -version = "0.24.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-rational", - "num-traits", - "png", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "jni" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[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.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" -dependencies = [ - "libc", -] - -[[package]] -name = "jpeg-decoder" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "lyon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f" -dependencies = [ - "lyon_algorithms", - "lyon_tessellation", -] - -[[package]] -name = "lyon_algorithms" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb7a1845c15729d73d25d42cb650b647f73c3494453a5c3cd3aae0df3ac5c6c" -dependencies = [ - "lyon_path", - "num-traits", -] - -[[package]] -name = "lyon_geom" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad" -dependencies = [ - "arrayvec 0.7.2", - "euclid", - "num-traits", -] - -[[package]] -name = "lyon_path" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8358c012e5651e4619cfd0b5b75c0f77866181a01b0909aab4bae14adf660" -dependencies = [ - "lyon_geom", - "num-traits", -] - -[[package]] -name = "lyon_tessellation" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e48a1d4a9cef1075ebe0afbf16521db6c7942e6563c136fb15676dafc5135d7" -dependencies = [ - "float_next_after", - "lyon_path", - "thiserror", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmap2" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[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.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "mint" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" - -[[package]] -name = "mio" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.42.0", -] - -[[package]] -name = "ndk" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" -dependencies = [ - "bitflags", - "jni-sys", - "ndk-sys 0.3.0", - "num_enum", - "thiserror", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags", - "jni-sys", - "ndk-sys 0.4.1+23.1.7779620", - "num_enum", - "raw-window-handle 0.5.0", - "thiserror", -] - -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - -[[package]] -name = "ndk-glue" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" -dependencies = [ - "libc", - "log", - "ndk 0.7.0", - "ndk-context", - "ndk-macro", - "ndk-sys 0.4.1+23.1.7779620", - "once_cell", - "parking_lot", -] - -[[package]] -name = "ndk-macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ndk-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "ndk-sys" -version = "0.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "nix" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "notan" -version = "0.9.5" -dependencies = [ - "bytemuck", - "egui_demo_lib", - "notan_app", - "notan_audio", - "notan_backend", - "notan_core", - "notan_draw", - "notan_egui", - "notan_extra", - "notan_glyph", - "notan_graphics", - "notan_input", - "notan_log", - "notan_macro", - "notan_math", - "notan_random", - "notan_text", - "notan_utils", -] - -[[package]] -name = "notan_app" -version = "0.9.5" -dependencies = [ - "bytemuck", - "downcast-rs", - "futures", - "futures-util", - "hashbrown 0.13.2", - "indexmap", - "js-sys", - "log", - "notan_audio", - "notan_core", - "notan_graphics", - "notan_input", - "notan_macro", - "notan_math", - "notan_utils", - "parking_lot", - "platter2", - "serde", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "notan_audio" -version = "0.9.5" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "notan_backend" -version = "0.9.5" -dependencies = [ - "notan_app", - "notan_web", - "notan_winit", -] - -[[package]] -name = "notan_core" -version = "0.9.5" -dependencies = [ - "serde", - "web-sys", -] - -[[package]] -name = "notan_draw" -version = "0.9.5" -dependencies = [ - "log", - "lyon", - "notan_app", - "notan_glyph", - "notan_graphics", - "notan_macro", - "notan_math", - "notan_text", - "serde", - "serde_json", -] - -[[package]] -name = "notan_egui" -version = "0.9.5" -dependencies = [ - "bytemuck", - "egui", - "log", - "notan_app", - "notan_core", - "notan_macro", -] - -[[package]] -name = "notan_extra" -version = "0.9.5" -dependencies = [ - "notan_app", - "spin_sleep", -] - -[[package]] -name = "notan_glow" -version = "0.9.5" -dependencies = [ - "bytemuck", - "glow", - "hashbrown 0.13.2", - "image", - "js-sys", - "log", - "notan_graphics", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "notan_glyph" -version = "0.9.5" -dependencies = [ - "bytemuck", - "glyph_brush", - "log", - "notan_app", - "notan_graphics", - "notan_math", -] - -[[package]] -name = "notan_graphics" -version = "0.9.5" -dependencies = [ - "bytemuck", - "glsl-layout", - "image", - "notan_macro", - "notan_math", - "notan_utils", - "parking_lot", - "web-sys", -] - -[[package]] -name = "notan_input" -version = "0.9.5" -dependencies = [ - "hashbrown 0.13.2", - "log", - "notan_core", - "notan_math", - "serde", -] - -[[package]] -name = "notan_log" -version = "0.9.5" -dependencies = [ - "console_log", - "fern", - "js-sys", - "log", - "notan_app", - "time", - "wasm-bindgen", -] - -[[package]] -name = "notan_macro" -version = "0.9.5" -dependencies = [ - "cfg_aliases", - "glsl-to-spirv", - "num", - "proc-macro2", - "quote", - "shaderc", - "spirv_cross", - "syn", -] - -[[package]] -name = "notan_math" -version = "0.9.5" -dependencies = [ - "glam", - "serde", -] - -[[package]] -name = "notan_oddio" -version = "0.9.5" -dependencies = [ - "cpal", - "hashbrown 0.13.2", - "log", - "notan_audio", - "oddio", - "symphonia", -] - -[[package]] -name = "notan_random" -version = "0.9.5" -dependencies = [ - "getrandom", - "rand", - "rand_pcg", -] - -[[package]] -name = "notan_text" -version = "0.9.5" -dependencies = [ - "hashbrown 0.13.2", - "lazy_static", - "log", - "notan_app", - "notan_glyph", - "notan_graphics", - "notan_math", - "parking_lot", -] - -[[package]] -name = "notan_utils" -version = "0.9.5" -dependencies = [ - "instant", - "js-sys", - "log", - "mime_guess", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "notan_web" -version = "0.9.5" -dependencies = [ - "console_error_panic_hook", - "futures-util", - "js-sys", - "log", - "notan_app", - "notan_audio", - "notan_core", - "notan_glow", - "notan_graphics", - "notan_oddio", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "notan_winit" -version = "0.9.5" -dependencies = [ - "arboard", - "glutin", - "glutin-winit", - "image", - "log", - "mime_guess", - "notan_app", - "notan_audio", - "notan_core", - "notan_glow", - "notan_input", - "notan_oddio", - "raw-window-handle 0.5.0", - "webbrowser", - "winit", -] - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "oboe" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" -dependencies = [ - "jni 0.19.0", - "ndk 0.6.0", - "ndk-context", - "num-derive", - "num-traits", - "oboe-sys", -] - -[[package]] -name = "oboe-sys" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd" -dependencies = [ - "cc", -] - -[[package]] -name = "oddio" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f3379cc7e854d0b0d4a0e310f276faafc46896843b720efe4646faa1b0c734" -dependencies = [ - "mint", -] - -[[package]] -name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "ordered-float" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf" -dependencies = [ - "num-traits", -] - -[[package]] -name = "owned_ttf_parser" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5f3c7ca08b6879e7965fb25e24d1f5eeb32ea73f9ad99b3854778a38c57e93" -dependencies = [ - "ttf-parser", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.42.0", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "platter2" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55ebe9054b9786c9bfe4dd9e8b5df8cd071284434979e9501baa612c5fa5a5e" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "png" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" -dependencies = [ - "bitflags", - "crc32fast", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" -dependencies = [ - "once_cell", - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_pcg" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" -dependencies = [ - "rand_core", -] - -[[package]] -name = "raw-window-handle" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" -dependencies = [ - "cty", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" -dependencies = [ - "cty", -] - -[[package]] -name = "rayon" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "safe_arch" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sctk-adwaita" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" -dependencies = [ - "crossfont", - "log", - "smithay-client-toolkit", - "tiny-skia", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "servo-fontconfig" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" -dependencies = [ - "libc", - "servo-fontconfig-sys", -] - -[[package]] -name = "servo-fontconfig-sys" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" -dependencies = [ - "expat-sys", - "freetype-sys", - "pkg-config", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" -dependencies = [ - "block-buffer", - "byte-tools", - "digest", - "fake-simd", -] - -[[package]] -name = "shaderc" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31cef52787a0db5108788ea20bed13d6bf4b96287c5c5201e55725f7070f3443" -dependencies = [ - "libc", - "shaderc-sys", -] - -[[package]] -name = "shaderc-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8f8439fffcffd6efcd74197204addf935dbab5752696bd990a6cd36d54cf64" -dependencies = [ - "cmake", - "libc", - "roxmltree", -] - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "smithay-client-toolkit" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" -dependencies = [ - "bitflags", - "calloop", - "dlib", - "lazy_static", - "log", - "memmap2", - "nix 0.24.3", - "pkg-config", - "wayland-client", - "wayland-cursor", - "wayland-protocols", -] - -[[package]] -name = "spin_sleep" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cafa7900db085f4354dbc7025e25d7a839a14360ea13b5fc4fd717f2d3b23134" -dependencies = [ - "once_cell", - "winapi", -] - -[[package]] -name = "spirv_cross" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60647fadbf83c4a72f0d7ea67a7ca3a81835cf442b8deae5c134c3e0055b2e14" -dependencies = [ - "cc", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "symphonia" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3671dd6f64f4f9d5c87179525054cfc1f60de23ba1f193bd6ceab812737403f1" -dependencies = [ - "lazy_static", - "symphonia-bundle-flac", - "symphonia-bundle-mp3", - "symphonia-codec-adpcm", - "symphonia-codec-pcm", - "symphonia-codec-vorbis", - "symphonia-core", - "symphonia-format-mkv", - "symphonia-format-ogg", - "symphonia-format-wav", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-bundle-flac" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc2deed3204967871ba60f913378f95820cb47a2fe9b2eef5a9eedb417dfdc8" -dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", -] - -[[package]] -name = "symphonia-bundle-mp3" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a0846e7a2c9a8081ff799fc83a975170417ad2a143f644a77ec2e3e82a2b73" -dependencies = [ - "bitflags", - "lazy_static", - "log", - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-codec-adpcm" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5cfb8d4405e26eb9593157dc45b05e102b8d774b38ed2a95946d6bb9e26e3e" -dependencies = [ - "log", - "symphonia-core", -] - -[[package]] -name = "symphonia-codec-pcm" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb9a9f0b9991cccf3217b74644af412d5d082a4815e5e2943f26e0ecabdf3c9" -dependencies = [ - "log", - "symphonia-core", -] - -[[package]] -name = "symphonia-codec-vorbis" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfed6f7b6bfa21d7cef1acefc8eae5db80df1608a1aca91871b07cbd28d7b74" -dependencies = [ - "log", - "symphonia-core", - "symphonia-utils-xiph", -] - -[[package]] -name = "symphonia-core" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9567e2d8a5f866b2f94f5d366d811e0c6826babcff6d37de9e1a6690d38869" -dependencies = [ - "arrayvec 0.7.2", - "bitflags", - "bytemuck", - "lazy_static", - "log", -] - -[[package]] -name = "symphonia-format-mkv" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd22f2def8c8f078495ad66111648bfc7d5222ee33774f2077cb665588f3119" -dependencies = [ - "lazy_static", - "log", - "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", -] - -[[package]] -name = "symphonia-format-ogg" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474df6e86b871dcb56913130bada1440245f483057c4a2d8a2981455494c4439" -dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", -] - -[[package]] -name = "symphonia-format-wav" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06679bd5646b3037300f88891dfc8a6e1cc4e1133206cc17a98e5d7c22f88296" -dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-metadata" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd35c263223ef6161000be79b124a75de3e065eea563bf3ef169b3e94c7bb2e" -dependencies = [ - "encoding_rs", - "lazy_static", - "log", - "symphonia-core", -] - -[[package]] -name = "symphonia-utils-xiph" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce340a6c33ac06cb42de01220308ec056e8a2a3d5cc664aaf34567392557136b" -dependencies = [ - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" -dependencies = [ - "itoa", - "libc", - "num_threads", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-skia" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "bytemuck", - "cfg-if", - "png", - "safe_arch", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" -dependencies = [ - "arrayref", - "bytemuck", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "ttf-parser" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "rand", - "static_assertions", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode_names2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446c96c6dd42604779487f0a981060717156648c1706aa1f464677f03c6cc059" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "wayland-client" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.24.3", - "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys 0.29.5", -] - -[[package]] -name = "wayland-commons" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" -dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", -] - -[[package]] -name = "wayland-cursor" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" -dependencies = [ - "nix 0.24.3", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-protocols" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" -dependencies = [ - "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", -] - -[[package]] -name = "wayland-sys" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" -dependencies = [ - "dlib", - "lazy_static", - "pkg-config", -] - -[[package]] -name = "wayland-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" -dependencies = [ - "dlib", - "lazy_static", - "log", - "pkg-config", -] - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74f5ff7786c4c21f61ba8e30ea29c9745f06fca0a4a02d083b3c662583399e8" -dependencies = [ - "core-foundation", - "dirs", - "jni 0.20.0", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.0", - "url", - "web-sys", - "windows 0.43.0", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-wsapoll" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" -dependencies = [ - "winapi", -] - -[[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.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - -[[package]] -name = "windows" -version = "0.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "winit" -version = "0.27.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" -dependencies = [ - "bitflags", - "cocoa", - "core-foundation", - "core-graphics", - "dispatch", - "instant", - "libc", - "log", - "mio", - "ndk 0.7.0", - "ndk-glue", - "objc", - "once_cell", - "parking_lot", - "percent-encoding", - "raw-window-handle 0.4.3", - "raw-window-handle 0.5.0", - "sctk-adwaita", - "smithay-client-toolkit", - "wasm-bindgen", - "wayland-client", - "wayland-protocols", - "web-sys", - "windows-sys 0.36.1", - "x11-dl", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[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.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" -dependencies = [ - "gethostname", - "nix 0.24.3", - "winapi", - "winapi-wsapoll", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" -dependencies = [ - "nix 0.24.3", -] - -[[package]] -name = "xcursor" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" -dependencies = [ - "nom", -] - -[[package]] -name = "xi-unicode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" - -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" - -[[package]] -name = "xmlparser" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" diff --git a/Cargo.toml b/Cargo.toml index 67e53996..8df25e68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,43 +1,85 @@ [package] name = "notan" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true categories = ["graphics", "rendering", "wasm", "gui", "multimedia"] keywords = ["notan", "engine", "gamedev", "gui", "windowing"] exclude = ["docs/**/*", "scripts/**/*", "crates/**/*"] readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "A simple portable multimedia layer to create apps or games easily" -[package.metadata.docs.rs] -features = ["default", "glyph", "egui", "text", "extra", "audio", "links", "drop_files", "clipboard", "save_file", "texture_to_file"] +[workspace] +members = ["crates/*", "xtask"] -[profile.release] -lto = true +[workspace.package] +version = "0.14.0" +authors = ["Nazarí González "] +license = "MIT OR Apache-2.0" +homepage = "https://github.com/Nazariglez/notan" +repository = "https://github.com/Nazariglez/notan" +edition = "2021" -[dependencies] -notan_core = { path = "crates/notan_core", version = "0.9.5" } -notan_input = { path = "crates/notan_input", version = "0.9.5" } -notan_app = { path = "crates/notan_app", version = "0.9.5" } -notan_macro = { path = "crates/notan_macro", version = "0.9.5" } -notan_math = { path = "crates/notan_math", version = "0.9.5" } -notan_graphics = { path = "crates/notan_graphics", version = "0.9.5" } -notan_utils = { path = "crates/notan_utils", version = "0.9.5" } -notan_log = { path = "crates/notan_log", version = "0.9.5", optional = true } -notan_glyph = { path = "crates/notan_glyph", version = "0.9.5", optional = true } -notan_draw = { path = "crates/notan_draw", version = "0.9.5", optional = true } -notan_backend = { path = "crates/notan_backend", version = "0.9.5", optional = true } -notan_egui = { path = "crates/notan_egui", version = "0.9.5", optional = true } -notan_text = { path = "crates/notan_text", version = "0.9.5", optional = true } -notan_audio = { path = "crates/notan_audio", version = "0.9.5", optional = true } -notan_extra = { path = "crates/notan_extra", version = "0.9.5", optional = true } -notan_random = { path = "crates/notan_random", version = "0.9.5", optional = true } +[workspace.dependencies] +notan_core = { path = "crates/notan_core", version = "0.14.0" } +notan_input = { path = "crates/notan_input", version = "0.14.0" } +notan_app = { path = "crates/notan_app", version = "0.14.0" } +notan_macro = { path = "crates/notan_macro", version = "0.14.0" } +notan_math = { path = "crates/notan_math", version = "0.14.0" } +notan_graphics = { path = "crates/notan_graphics", version = "0.14.0" } +notan_utils = { path = "crates/notan_utils", version = "0.14.0" } +notan_log = { path = "crates/notan_log", version = "0.14.0" } +notan_glyph = { path = "crates/notan_glyph", version = "0.14.0" } +notan_draw = { path = "crates/notan_draw", version = "0.14.0" } +notan_backend = { path = "crates/notan_backend", version = "0.14.0" } +notan_egui = { path = "crates/notan_egui", version = "0.14.0" } +notan_text = { path = "crates/notan_text", version = "0.14.0" } +notan_audio = { path = "crates/notan_audio", version = "0.14.0" } +notan_extra = { path = "crates/notan_extra", version = "0.14.0" } +notan_random = { path = "crates/notan_random", version = "0.14.0" } +notan_winit = { path = "crates/notan_winit", version = "0.14.0" } +notan_web = { path = "crates/notan_web", version = "0.14.0" } +notan_glow = { path = "crates/notan_glow", version = "0.14.0" } +notan_oddio = { path = "crates/notan_oddio", version = "0.14.0" } + +log = "0.4.25" +hashbrown = "0.16.1" +parking_lot = "0.12.3" +bytemuck = "1.21.0" +serde = { version = "1.0", features = ["serde_derive"] } +image = { version = "0.25.5", default-features = false, features = [ + "jpeg", + "png", + "ico", +] } + +# wasm deps +wasm-bindgen = "0.2.100" +wasm-bindgen-futures = "0.4.50" +futures-util = "0.3.31" +web-sys = "0.3.77" +js-sys = "0.3.77" -[workspace] -members = ["crates/*"] +[dependencies] +notan_core.workspace = true +notan_input.workspace = true +notan_app.workspace = true +notan_macro.workspace = true +notan_math.workspace = true +notan_graphics.workspace = true +notan_utils.workspace = true +notan_log = { workspace = true, optional = true } +notan_glyph = { workspace = true, optional = true } +notan_draw = { workspace = true, optional = true } +notan_backend = { workspace = true, optional = true } +notan_egui = { workspace = true, optional = true } +notan_text = { workspace = true, optional = true } +notan_audio = { workspace = true, optional = true } +notan_extra = { workspace = true, optional = true } +notan_random = { workspace = true, optional = true } [features] default = ["backend", "log", "draw", "random", "shaderc"] @@ -50,18 +92,59 @@ text = ["notan_text"] extra = ["notan_extra"] audio = ["notan_audio", "notan_app/audio", "notan_backend?/audio"] links = ["notan_app/links", "notan_backend?/links", "notan_egui?/links"] -drop_files = ["notan_app/drop_files", "notan_backend?/drop_files", "notan_egui?/drop_files"] +drop_files = [ + "notan_app/drop_files", + "notan_backend?/drop_files", + "notan_egui?/drop_files", +] clipboard = ["notan_app/clipboard", "notan_backend?/clipboard"] save_file = ["notan_utils/save_file"] texture_to_file = ["notan_graphics/texture_to_file"] random = ["notan_random"] -glsl-to-spirv = ["notan_macro/glsl-to-spirv"] -shaderc = ["notan_macro/shaderc"] -serde = ["notan_app/serde", "notan_math/serde", "notan_core/serde", "notan_input/serde"] +glsl-to-spirv = [ + "notan_macro/glsl-to-spirv", + "notan_glyph?/glsl-to-spirv", + "notan_draw?/glsl-to-spirv", + "notan_egui?/glsl-to-spirv", + "notan_text?/glsl-to-spirv", +] +shaderc = [ + "notan_macro/shaderc", + "notan_glyph?/shaderc", + "notan_draw?/shaderc", + "notan_egui?/shaderc", + "notan_text?/shaderc", +] +serde = [ + "notan_app/serde", + "notan_math/serde", + "notan_core/serde", + "notan_input/serde", + "notan_graphics/serde", + "notan_egui?/serde", +] + +[package.metadata.docs.rs] +features = [ + "default", + "glyph", + "egui", + "text", + "extra", + "audio", + "links", + "drop_files", + "clipboard", + "save_file", + "texture_to_file", +] + +[profile.release] +lto = true [dev-dependencies] -egui_demo_lib = "0.20.0" -bytemuck = "1.13.0" +egui_demo_lib = "0.33.0" +bytemuck = "1.21.0" [[example]] name = "app_drop_file" @@ -155,6 +238,10 @@ required-features = ["draw"] name = "draw_pattern" required-features = ["draw"] +[[example]] +name = "draw_points" +required-features = ["draw"] + [[example]] name = "draw_projection" required-features = ["draw"] @@ -199,6 +286,10 @@ required-features = ["draw"] name = "egui_basic" required-features = ["egui"] +[[example]] +name = "egui_custom_font" +required-features = ["egui"] + [[example]] name = "egui_demo" required-features = ["egui", "links"] diff --git a/README.md b/README.md index c0c59f9d..cd40b5a2 100644 --- a/README.md +++ b/README.md @@ -6,36 +6,39 @@

- - + + - - + + - - + + - - + + -
- -__Notan__ is a simple and portable layer, designed to make your own multimedia apps on top of it +**Notan** is a simple and portable layer, designed to make your own multimedia apps on top of it without worrying too much about platform-specific code. The main goal is to provide a set of APIs and tools that can be used to create your project in an ergonomic manner without enforcing any structure or pattern, always trying to stay out of your way. -## Community +## Community - [Discord](https://discord.gg/rH3nP7neeu): Join us! +## Status + +Notan is in slow maintenance mode. I'm focusing on other projects for now. +For more details -> [Notan 1.x and Next Steps](https://github.com/nazariglez/notan/issues/354) + ## Examples -* [Online demos](https://nazariglez.github.io/notan-web/). +- [Online demos](https://nazariglez.github.io/notan-web/). #### Do you want to open a window? @@ -161,7 +164,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(gfx: &mut Graphics, state: &mut State) { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffer(&state.vbo); renderer.draw(0, 3); @@ -181,47 +184,50 @@ Add `notan` to your project from [crates.io](https://crates.io). The `main` bran ## WebAssembly -We treat the web as a first class citizen. WebAssembly compilation is dead simple, we recommend to just use [trunk](https://trunkrs.dev/). -You only need to add an `index.html` file to your project and run `trunk serve` to see it working. +We treat the web as a first class citizen. WebAssembly compilation is dead simple, we recommend to just use [trunk](https://trunkrs.dev/). +You only need to add an `index.html` file to your project and run `trunk serve` to see it working. Here is a simple `index.html` file as an example: ```html - - Notan App - - - - - - - - - + + Notan App + + + + + + + + + ``` -However, you can also use [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) or [wasm-pack](https://rustwasm.github.io/wasm-pack/). +However, you can also use [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) or [wasm-pack](https://rustwasm.github.io/wasm-pack/). ## How it works @@ -248,7 +254,7 @@ The current graphics backend in place for these platforms is using [glow.rs](htt People love to see performance numbers and benchmarks (I love it too), but the truth is that any benchmark or numbers worth nothing without the full context of how these numbers were calculated. -We didn't check (*yet*) about which parts of the code can be changed to improve the performance. +We didn't check (_yet_) about which parts of the code can be changed to improve the performance. Is not an easy task to keep in balance a good API, small boilerplate, and been performant. However, this is something that we try to accomplish with this project since the idea was born. @@ -258,23 +264,25 @@ Let's see a simple example, the 2D Draw API is built on top of the Graphics API, but I got some decent numbers on my machine running the example [draw_bunnymark](examples/draw_bunnymark.rs). On a Macbook (2.3Hz i9 - 16GB RAM): + - Native: 85000 Bunnies at 60FPS - Chrome: 78000 Bunnies at 60FPS On a high-end Desktop with Archlinux: + - Native: 205000 Bunnies at 60FPS - Chrome: 131000 Bunnies at 60FPS Let's keep in mind that the conditions for `bunnymark` are very unlikely to see in a real project. However, it's widely used to test the performance in 2D Draw APIs. -## Integration +## Integration -Notan is designed to be as modular as possible. It's flexible enough to allow change how the event life cycle works with -a plugin (i.e: [FpsLimit](crates/notan_extra/src/fps_limit.rs)), or to allow us to draw custom things easily on top of the -graphics API using *Graphic Extensions* (i.e: [egui](crates/notan_egui) or [draw](crates/notan_draw)). +Notan is designed to be as modular as possible. It's flexible enough to allow change how the event life cycle works with +a plugin (i.e: [FpsLimit](crates/notan_extra/src/fps_limit.rs)), or to allow us to draw custom things easily on top of the +graphics API using _Graphic Extensions_ (i.e: [egui](crates/notan_egui) or [draw](crates/notan_draw)). -Even any backend can be easily *plugged-in* from the code just using `init_with_backend`. +Even any backend can be easily _plugged-in_ from the code just using `init_with_backend`. We include some of these plugins or graphics extensions behind feature flags, as a part of the project. However, everybody can create their own plugins or extension to extend Notan. @@ -286,7 +294,7 @@ not been too much opinionated about how to do it, with multiple platforms suppor I felt that it was a tricky thing to find until I found [Haxe](https://haxe.org/) and [Kha](https://kha.tech/), the perfect match. However, I did not like a few things about the build system, the lack of tools and IDEs, and how the language itself does some things. -So, after a while I decided to start looking again, and I saw that **Rust** had a great **WebAssembly** compiler among other targets, +So, after a while I decided to start looking again, and I saw that **Rust** had a great **WebAssembly** compiler among other targets, and check all those boxes. For the last three years, I have been working on this project in different repositories with different names and multiple "start-over" times. @@ -294,21 +302,22 @@ It was my place to learn Rust and OpenGL, you can say that it was my sandbox and However, I feel that it could be useful for more people than me in the current state. -The name **Notan** comes from `not an engine`. The main purpose of the project is to be used as foundation providing -a basic but useful set of features. +The name **Notan** comes from `not an engine`. The main purpose of the project is to be used as foundation providing +a basic but useful set of features. They are: + - Platform abstraction (desktop, mobile, etc...) - Windowing - Graphics rendering - Text rendering - Draw 2D API -- Audio +- Audio - Input (Keyboard, mouse, etc...) - Simple UI via egui Everything else, like particles, physics, etc..., is out of the scope of the project -and can be added via external crates, plugins or extensions. Although we can always talk +and can be added via external crates, plugins or extensions. Although we can always talk if you think that something should be part of the project. ## So... what's next? diff --git a/crates/notan_app/Cargo.toml b/crates/notan_app/Cargo.toml index 624de46f..4e562001 100644 --- a/crates/notan_app/Cargo.toml +++ b/crates/notan_app/Cargo.toml @@ -1,42 +1,42 @@ [package] name = "notan_app" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides the core API for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -hashbrown = "0.13.2" -notan_core = { path = "../notan_core", version = "0.9.5" } -notan_input = { path = "../notan_input", version = "0.9.5" } -notan_math = { path = "../notan_math", version = "0.9.5" } -notan_macro = { path = "../notan_macro", version = "0.9.5" } -notan_graphics = { path = "../notan_graphics", version = "0.9.5" } -notan_utils = { path = "../notan_utils", version = "0.9.5" } -notan_audio = { path = "../notan_audio", version = "0.9.5", optional = true } -downcast-rs = "1.2.0" -indexmap = "1.9.2" -futures = "0.3.25" -parking_lot = "0.12.1" -bytemuck = "1.13.0" -serde = { version = "1", optional = true, features = ["serde_derive"] } +notan_core.workspace = true +notan_input.workspace = true +notan_macro.workspace = true +notan_graphics.workspace = true +notan_utils.workspace = true +notan_audio = { workspace = true, optional = true } + +log.workspace = true +hashbrown.workspace = true +parking_lot.workspace = true +serde = { workspace = true, optional = true } + +downcast-rs = "2.0.1" +indexmap = "2.7.1" +futures = "0.3.31" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] platter2 = "0.1.6" [target.'cfg(target_arch = "wasm32")'.dependencies] -web-sys = { version = "0.3.60", optional = true } -wasm-bindgen-futures = { version = "0.4.33", optional = true } -futures-util = { version = "0.3.25", optional = true } -js-sys = { version = "0.3.60", optional = true } platter2 = { version = "0.1.6", features = ["web-sys"] } +web-sys = { workspace = true, optional = true } +wasm-bindgen-futures = { workspace = true, optional = true } +futures-util = { workspace = true, optional = true } +js-sys = { workspace = true, optional = true } [features] audio = ["notan_audio"] diff --git a/crates/notan_app/src/app.rs b/crates/notan_app/src/app.rs index e316fde5..e1a6aad6 100644 --- a/crates/notan_app/src/app.rs +++ b/crates/notan_app/src/app.rs @@ -1,4 +1,4 @@ -use crate::timer::AppTimer; +pub use crate::timer::AppTimer; use crate::{Backend, WindowBackend}; #[cfg(feature = "audio")] diff --git a/crates/notan_app/src/assets/list.rs b/crates/notan_app/src/assets/list.rs index ee6eaafd..37b466b1 100644 --- a/crates/notan_app/src/assets/list.rs +++ b/crates/notan_app/src/assets/list.rs @@ -6,6 +6,7 @@ use std::any::{Any, TypeId}; use std::sync::Arc; +#[derive(Clone)] pub struct AssetList { count: usize, load_tracker: HashMap, diff --git a/crates/notan_app/src/assets/manager.rs b/crates/notan_app/src/assets/manager.rs index ceb2f7e1..73ad6fff 100644 --- a/crates/notan_app/src/assets/manager.rs +++ b/crates/notan_app/src/assets/manager.rs @@ -44,10 +44,7 @@ impl Assets { let loader = match self.loaders.get(ext) { Some(loader) => loader, None => { - log::warn!( - "Not found a loader for '{}', loading as bytes (Vec)", - id - ); + log::warn!("Not found a loader for '{id}', loading as bytes (Vec)"); &self.byte_loader } }; @@ -64,7 +61,7 @@ impl Assets { pub fn add_loader(&mut self, loader: AssetLoader) { if let Err(e) = loader.apply(self) { - log::error!("{}", e); + log::error!("{e}"); } } @@ -77,10 +74,7 @@ impl Assets { let loader = match self.loaders.get(ext) { Some(loader) => loader, None => { - log::warn!( - "Not found a loader for '{}', loading as bytes (Vec)", - id - ); + log::warn!("Not found a loader for '{id}', loading as bytes (Vec)"); &self.byte_loader } }; diff --git a/crates/notan_app/src/assets/storage.rs b/crates/notan_app/src/assets/storage.rs index 21113508..cf5904b0 100644 --- a/crates/notan_app/src/assets/storage.rs +++ b/crates/notan_app/src/assets/storage.rs @@ -61,7 +61,7 @@ impl AssetStorage { stored_asset.loaded.done(); }) .map_err(|e| { - log::error!("{}", e); + log::error!("{e}"); e }) } diff --git a/crates/notan_app/src/backend.rs b/crates/notan_app/src/backend.rs index a3af806d..bf35f832 100644 --- a/crates/notan_app/src/backend.rs +++ b/crates/notan_app/src/backend.rs @@ -49,14 +49,26 @@ pub enum FrameState { Skip, } +pub trait AppRunner { + fn run(&mut self) -> Result; + fn app(&self) -> &App; + fn app_mut(&mut self) -> &mut App; +} + +pub trait AppLoader { + fn load(self: Box) -> Result, String>; + fn backend(&mut self) -> &mut dyn Backend; +} + +/// A closure that initializes and runs the app +pub trait BackendRunner { + fn run(&mut self, app_loader: Box, config: WindowConfig) -> Result<(), String>; +} + /// Backend initialization run pub trait BackendSystem: Backend { - /// Returns a closure where the backend is initialized and the application loops is managed - fn initialize(&mut self, window: WindowConfig) -> Result>, String> - where - Self: Backend, - S: 'static, - R: FnMut(&mut App, &mut S) -> Result + 'static; + /// Returns a closure that initializes and runs the app + fn runner(&self) -> Box; /// Returns a function that load files fn get_file_loader(&self) -> LoadFileFn { @@ -118,7 +130,7 @@ pub trait WindowBackend { fn capture_cursor(&self) -> bool; /// Returns the container size. Meaning the screen size on native - /// and the canva's parent element size on `web` + /// and the canvas's parent element size on `web` fn container_size(&self) -> (i32, i32) { self.screen_size() } @@ -130,7 +142,7 @@ pub trait WindowBackend { fn dpi(&self) -> f64; /// Window's height - fn height(&self) -> i32 { + fn height(&self) -> u32 { self.size().1 } @@ -143,10 +155,13 @@ pub trait WindowBackend { /// Returns true if the window is in fullscreen mode fn is_fullscreen(&self) -> bool; + /// Returns true if the window has focus + fn is_focused(&self) -> bool; + /// Returns true if the lazy mode is enabled fn lazy_loop(&self) -> bool; - // returns whether you can click through the window + /// Returns whether you can click through the window fn mouse_passthrough(&mut self) -> bool; /// Returns the window's position @@ -167,38 +182,41 @@ pub trait WindowBackend { /// Sets the mouse cursor icon fn set_cursor(&mut self, cursor: CursorIcon); + /// Sets the mouse cursor position + fn set_cursor_position(&mut self, x: f32, y: f32); + /// Enable or disable the fullscreen mode fn set_fullscreen(&mut self, enabled: bool); /// Enable or disable the lazy mode for the app's loop fn set_lazy_loop(&mut self, lazy: bool); - // sets whether you can click through the window + /// Sets whether you can click through the window fn set_mouse_passthrough(&mut self, pass_through: bool); /// Sets the window's position fn set_position(&mut self, x: i32, y: i32); /// Sets the window's size - fn set_size(&mut self, width: i32, height: i32); + fn set_size(&mut self, width: u32, height: u32); /// Hide or show the window fn set_visible(&mut self, visible: bool); - /// Returns the window's size - fn size(&self) -> (i32, i32); - /// Set the window's title fn set_title(&mut self, title: &str); /// Returns current windows title fn title(&self) -> &str; + /// Returns the window's size + fn size(&self) -> (u32, u32); + /// Returns if the window is visible fn visible(&self) -> bool; /// Window's width - fn width(&self) -> i32 { + fn width(&self) -> u32 { self.size().0 } diff --git a/crates/notan_app/src/builder.rs b/crates/notan_app/src/builder.rs index d2d9915a..36cdab8e 100644 --- a/crates/notan_app/src/builder.rs +++ b/crates/notan_app/src/builder.rs @@ -1,7 +1,6 @@ #![allow(clippy::type_complexity)] use crate::assets::{AssetLoader, Assets}; -use crate::config::*; use crate::graphics::Graphics; use crate::handlers::{ AppCallback, AppHandler, DrawCallback, DrawHandler, EventCallback, EventHandler, @@ -9,6 +8,7 @@ use crate::handlers::{ }; use crate::parsers::*; use crate::plugins::*; +use crate::{config::*, AppLoader, AppRunner}; use crate::{App, Backend, BackendSystem, FrameState, GfxExtension, GfxRenderer}; use indexmap::IndexMap; #[cfg(feature = "audio")] @@ -59,6 +59,258 @@ pub struct AppBuilder { pub(crate) window: WindowConfig, } +struct Runner { + app: App, + graphics: Graphics, + plugins: Plugins, + assets: Assets, + state: S, + current_touch_id: Option, + event_callback: Option>, + update_callback: Option>, + draw_callback: Option>, + first_loop: bool, +} + +impl AppLoader for AppBuilder { + fn backend(&mut self) -> &mut dyn Backend { + &mut self.backend + } + + fn load(self: Box) -> Result, String> { + let AppBuilder { + backend, + setup_callback, + mut plugins, + mut assets, + + init_callback, + update_callback, + draw_callback, + event_callback, + mut plugin_callbacks, + mut extension_callbacks, + use_touch_as_mouse, + .. + } = *self; + + let mut graphics = Graphics::new(backend.get_graphics_backend())?; + + #[cfg(feature = "audio")] + let audio = Audio::new(backend.get_audio_backend())?; + #[cfg(feature = "audio")] + let mut app = App::new(Box::new(backend), audio); + + #[cfg(not(feature = "audio"))] + let mut app = App::new(Box::new(backend)); + + app.window().set_touch_as_mouse(use_touch_as_mouse); + + let (width, height) = app.window().size(); + let win_dpi = app.window().dpi(); + graphics.set_size(width, height); + graphics.set_dpi(win_dpi); + + // add graphics extensions + extension_callbacks.reverse(); + while let Some(cb) = extension_callbacks.pop() { + cb(&mut app, &mut assets, &mut graphics, &mut plugins); + } + + // add plugins + plugin_callbacks.reverse(); + while let Some(cb) = plugin_callbacks.pop() { + cb(&mut app, &mut assets, &mut graphics, &mut plugins); + } + + // create the state + let mut state = setup_callback.exec(&mut app, &mut assets, &mut graphics, &mut plugins); + + // init callback from plugins + let _ = plugins.init(&mut app, &mut assets, &mut graphics).map(|flow| match flow { + AppFlow::Next => Ok(()), + _ => Err(format!( + "Aborted application loop because a plugin returns on the init method AppFlow::{flow:?} instead of AppFlow::Next", + )), + })?; + + // app init life event + if let Some(cb) = init_callback { + cb.exec(&mut app, &mut assets, &mut plugins, &mut state); + } + + Ok(Box::new(Runner { + app, + graphics, + plugins, + assets, + state, + current_touch_id: None, + event_callback, + update_callback, + draw_callback, + first_loop: true, + })) + } +} + +impl AppRunner for Runner { + fn run(&mut self) -> Result { + self.app.system_timer.update(); + + let win_size = self.app.window().size(); + if self.graphics.size() != win_size { + let (width, height) = win_size; + self.graphics.set_size(width, height); + } + + let win_dpi = self.app.window().dpi(); + if (self.graphics.dpi() - win_dpi).abs() > f64::EPSILON { + self.graphics.set_dpi(win_dpi); + } + + // Manage pre frame events + if let AppFlow::SkipFrame = + self.plugins + .pre_frame(&mut self.app, &mut self.assets, &mut self.graphics)? + { + return Ok(FrameState::Skip); + } + + // update delta time and fps here + self.app.timer.update(); + + self.assets.tick(( + &mut self.app, + &mut self.graphics, + &mut self.plugins, + &mut self.state, + ))?; + + let delta = self.app.timer.delta_f32(); + + let use_touch_as_mouse = self.app.window().touch_as_mouse(); + + // Manage each event + let mut events = self.app.backend.events_iter(); + while let Some(evt) = events.next() { + if use_touch_as_mouse { + touch_as_mouse(&mut self.current_touch_id, &mut events, &evt); + } + + process_keyboard_events(&mut self.app.keyboard, &evt, delta); + process_mouse_events(&mut self.app.mouse, &evt, delta); + process_touch_events(&mut self.app.touch, &evt, delta); + + match self.plugins.event(&mut self.app, &mut self.assets, &evt)? { + AppFlow::Skip => {} + AppFlow::Next => { + if let Some(cb) = &self.event_callback { + cb.exec( + &mut self.app, + &mut self.assets, + &mut self.plugins, + &mut self.state, + evt, + ); + } + } + AppFlow::SkipFrame => return Ok(FrameState::Skip), + } + } + + // Manage update callback + match self.plugins.update(&mut self.app, &mut self.assets)? { + AppFlow::Skip => {} + AppFlow::Next => { + if let Some(cb) = &self.update_callback { + cb.exec( + &mut self.app, + &mut self.assets, + &mut self.plugins, + &mut self.state, + ); + } + } + AppFlow::SkipFrame => return Ok(FrameState::Skip), + } + + // Manage draw callback + match self + .plugins + .draw(&mut self.app, &mut self.assets, &mut self.graphics)? + { + AppFlow::Skip => {} + AppFlow::Next => { + if let Some(cb) = &self.draw_callback { + cb.exec( + &mut self.app, + &mut self.assets, + &mut self.graphics, + &mut self.plugins, + &mut self.state, + ); + } + } + AppFlow::SkipFrame => return Ok(FrameState::Skip), + } + + // call next frame in lazy mode if user is pressing mouse or keyboard + if self.app.window().lazy_loop() { + let mouse_down = !self.app.mouse.down.is_empty(); + let key_down = !self.app.keyboard.down.is_empty(); + if mouse_down || key_down { + self.app.window().request_frame(); + } + } + + clear_mouse(&mut self.app.mouse); + clear_keyboard(&mut self.app.keyboard); + + // Manage post frame event + let _ = self + .plugins + .post_frame(&mut self.app, &mut self.assets, &mut self.graphics)?; + + // Clean possible dropped resources on the backend + self.graphics.clean(); + #[cfg(feature = "audio")] + self.app.audio.clean(); + + // dispatch Event::Exit before close the app + if self.app.closed { + let evt = Event::Exit; + let _ = self.plugins.event(&mut self.app, &mut self.assets, &evt)?; + if let Some(cb) = &self.event_callback { + cb.exec( + &mut self.app, + &mut self.assets, + &mut self.plugins, + &mut self.state, + evt, + ); + } + } + + // Using lazy loop we need to draw 2 frames at the beginning to avoid + // a blank window when the buffer is swapped + if !self.app.closed && self.app.window().lazy_loop() && self.first_loop { + self.first_loop = false; + self.app.window().request_frame(); + } + + Ok(FrameState::End) + } + + fn app(&self) -> &App { + &self.app + } + + fn app_mut(&mut self) -> &mut App { + &mut self.app + } +} + impl AppBuilder where S: 'static, @@ -214,188 +466,9 @@ where } } - let AppBuilder { - mut backend, - setup_callback, - mut plugins, - mut assets, - - init_callback, - update_callback, - draw_callback, - event_callback, - mut plugin_callbacks, - mut extension_callbacks, - window, - use_touch_as_mouse, - .. - } = builder; - - let initialize = backend.initialize(window)?; - - let mut graphics = Graphics::new(backend.get_graphics_backend())?; - - #[cfg(feature = "audio")] - let audio = Audio::new(backend.get_audio_backend())?; - #[cfg(feature = "audio")] - let mut app = App::new(Box::new(backend), audio); - - #[cfg(not(feature = "audio"))] - let mut app = App::new(Box::new(backend)); - - app.window().set_touch_as_mouse(use_touch_as_mouse); - - let (width, height) = app.window().size(); - let win_dpi = app.window().dpi(); - graphics.set_size(width, height); - graphics.set_dpi(win_dpi); - - // add graphics extensions - extension_callbacks.reverse(); - while let Some(cb) = extension_callbacks.pop() { - cb(&mut app, &mut assets, &mut graphics, &mut plugins); - } - - // add plugins - plugin_callbacks.reverse(); - while let Some(cb) = plugin_callbacks.pop() { - cb(&mut app, &mut assets, &mut graphics, &mut plugins); - } - - // create the state - let mut state = setup_callback.exec(&mut app, &mut assets, &mut graphics, &mut plugins); - - // init callback from plugins - let _ = plugins.init(&mut app, &mut assets, &mut graphics).map(|flow| match flow { - AppFlow::Next => Ok(()), - _ => Err(format!( - "Aborted application loop because a plugin returns on the init method AppFlow::{flow:?} instead of AppFlow::Next", - )), - })?; - - // app init life event - if let Some(cb) = init_callback { - cb.exec(&mut app, &mut assets, &mut plugins, &mut state); - } - - let mut current_touch_id: Option = None; - - let mut first_loop = true; - if let Err(e) = initialize(app, state, move |app, mut state| { - // update system delta time and fps here - app.system_timer.update(); - - let win_size = app.window().size(); - if graphics.size() != win_size { - let (width, height) = win_size; - graphics.set_size(width, height); - } - - let win_dpi = app.window().dpi(); - if (graphics.dpi() - win_dpi).abs() > f64::EPSILON { - graphics.set_dpi(win_dpi); - } - - // Manage pre frame events - if let AppFlow::SkipFrame = plugins.pre_frame(app, &mut assets, &mut graphics)? { - return Ok(FrameState::Skip); - } - - // update delta time and fps here - app.timer.update(); - - assets.tick((app, &mut graphics, &mut plugins, &mut state))?; - - let delta = app.timer.delta_f32(); - - let use_touch_as_mouse = app.window().touch_as_mouse(); - - // Manage each event - let mut events = app.backend.events_iter(); - while let Some(evt) = events.next() { - if use_touch_as_mouse { - touch_as_mouse(&mut current_touch_id, &mut events, &evt); - } - - process_keyboard_events(&mut app.keyboard, &evt, delta); - process_mouse_events(&mut app.mouse, &evt, delta); - process_touch_events(&mut app.touch, &evt, delta); - - match plugins.event(app, &mut assets, &evt)? { - AppFlow::Skip => {} - AppFlow::Next => { - if let Some(cb) = &event_callback { - cb.exec(app, &mut assets, &mut plugins, state, evt); - } - } - AppFlow::SkipFrame => return Ok(FrameState::Skip), - } - } - - // Manage update callback - match plugins.update(app, &mut assets)? { - AppFlow::Skip => {} - AppFlow::Next => { - if let Some(cb) = &update_callback { - cb.exec(app, &mut assets, &mut plugins, state); - } - } - AppFlow::SkipFrame => return Ok(FrameState::Skip), - } - - // Manage draw callback - match plugins.draw(app, &mut assets, &mut graphics)? { - AppFlow::Skip => {} - AppFlow::Next => { - if let Some(cb) = &draw_callback { - cb.exec(app, &mut assets, &mut graphics, &mut plugins, state); - } - } - AppFlow::SkipFrame => return Ok(FrameState::Skip), - } - - // call next frame in lazy mode if user is pressing mouse or keyboard - if app.window().lazy_loop() { - let mouse_down = !app.mouse.down.is_empty(); - let key_down = !app.keyboard.down.is_empty(); - if mouse_down || key_down { - app.window().request_frame(); - } - } - - clear_mouse(&mut app.mouse); - clear_keyboard(&mut app.keyboard); - - // Manage post frame event - let _ = plugins.post_frame(app, &mut assets, &mut graphics)?; - - // Clean possible dropped resources on the backend - graphics.clean(); - #[cfg(feature = "audio")] - app.audio.clean(); - - // dispatch Event::Exit before close the app - if app.closed { - let evt = Event::Exit; - let _ = plugins.event(app, &mut assets, &evt)?; - if let Some(cb) = &event_callback { - cb.exec(app, &mut assets, &mut plugins, state, evt); - } - } - - // Using lazy loop we need to draw 2 frames at the beginning to avoid - // a blank window when the buffer is swapped - if !app.closed && app.window().lazy_loop() && first_loop { - first_loop = false; - app.window().request_frame(); - } - - Ok(FrameState::End) - }) { - log::error!("{}", e); - } - - Ok(()) + let mut runner = builder.backend.runner(); + let window_config = builder.window.clone(); + runner.run(Box::new(builder), window_config) } } diff --git a/crates/notan_app/src/config.rs b/crates/notan_app/src/config.rs index 4d09bf8a..284ca9ef 100644 --- a/crates/notan_app/src/config.rs +++ b/crates/notan_app/src/config.rs @@ -12,20 +12,23 @@ pub struct WindowConfig { pub title: String, /// Window's width - pub width: i32, + pub width: u32, /// Window's height - pub height: i32, + pub height: u32, /// Start window in fullscreen mode /// `Web: no-op` pub fullscreen: bool, /// Minimum resizable window's size - pub min_size: Option<(i32, i32)>, + pub min_size: Option<(u32, u32)>, /// Maximum resizable window's size - pub max_size: Option<(i32, i32)>, + pub max_size: Option<(u32, u32)>, + + /// Window's x/y start position + pub position: Option<(i32, i32)>, /// Start the window maximized /// `Web: The canvas will fill the size of the parent of the HtmlCanvasElement` @@ -39,8 +42,8 @@ pub struct WindowConfig { /// `Web: no-op` pub vsync: bool, - /// Antialias nultisamples level - /// `Web: WebGL will use this as antialias = false if the value is 0 or true otherwise` + /// Sets multisampling + /// Setting to 0 disables multisampling pub multisampling: u8, /// Enable High DPI viewport and drawing if the device pixel ratio is higher than 1 @@ -67,8 +70,9 @@ pub struct WindowConfig { // Whether mouse events will pass through the window, useful for overlays pub mouse_passthrough: bool, - /// Use or create the canvas with this id. Only Web. - pub canvas_id: String, + /// App ID + /// On Web use or create the canvas with this id. + pub app_id: String, /// Optinal Window icon filepath pub window_icon_path: Option, @@ -92,6 +96,7 @@ impl Default for WindowConfig { fullscreen: false, min_size: None, max_size: None, + position: None, maximized: false, resizable: false, vsync: false, @@ -103,7 +108,7 @@ impl Default for WindowConfig { decorations: true, visible: true, mouse_passthrough: false, - canvas_id: String::from("notan_canvas"), + app_id: String::from("notan_app"), window_icon_path: None, window_icon_data: None, taskbar_icon_path: None, @@ -119,110 +124,118 @@ impl WindowConfig { } /// Sets the window's title - pub fn title(mut self, title: &str) -> Self { + pub fn set_title(mut self, title: &str) -> Self { self.title = title.to_string(); self } /// Inner loop will run only after an input event - pub fn lazy_loop(mut self, lazy: bool) -> Self { + pub fn set_lazy_loop(mut self, lazy: bool) -> Self { self.lazy_loop = lazy; self } /// Sets the window's width and height - pub fn size(mut self, width: i32, height: i32) -> Self { + pub fn set_size(mut self, width: u32, height: u32) -> Self { self.width = width; self.height = height; self } /// Enable fullscreen mode - pub fn fullscreen(mut self, fullscreen: bool) -> Self { + pub fn set_fullscreen(mut self, fullscreen: bool) -> Self { self.fullscreen = fullscreen; self } /// Sets the window's minimum size - pub fn min_size(mut self, width: i32, height: i32) -> Self { + pub fn set_min_size(mut self, width: u32, height: u32) -> Self { self.min_size = Some((width, height)); self } /// Sets the window's maximum size - pub fn max_size(mut self, width: i32, height: i32) -> Self { + pub fn set_max_size(mut self, width: u32, height: u32) -> Self { self.max_size = Some((width, height)); self } + /// Sets the window's x and y + pub fn set_position(mut self, x: i32, y: i32) -> Self { + self.position = Some((x, y)); + self + } + /// Starts the window maximized - pub fn maximized(mut self, maximized: bool) -> Self { + pub fn set_maximized(mut self, maximized: bool) -> Self { self.maximized = maximized; self } /// Allow the window to be resizable - pub fn resizable(mut self, resizable: bool) -> Self { + pub fn set_resizable(mut self, resizable: bool) -> Self { self.resizable = resizable; self } /// Enable vsync - pub fn vsync(mut self, vsync: bool) -> Self { + pub fn set_vsync(mut self, vsync: bool) -> Self { self.vsync = vsync; self } - /// Enabled multisampling aliasing (opengl) - pub fn multisampling(mut self, samples: u8) -> Self { + /// Sets multisampling + /// Setting to 0 disables multisampling + pub fn set_multisampling(mut self, samples: u8) -> Self { self.multisampling = samples; self } /// Enable High DPI - pub fn high_dpi(mut self, enabled: bool) -> Self { + pub fn set_high_dpi(mut self, enabled: bool) -> Self { self.high_dpi = enabled; self } /// Set the background as transparent - pub fn transparent(mut self, transparent: bool) -> Self { + pub fn set_transparent(mut self, transparent: bool) -> Self { self.transparent = transparent; self } /// Set the background as transparent - pub fn always_on_top(mut self, always_on_top: bool) -> Self { + pub fn set_always_on_top(mut self, always_on_top: bool) -> Self { self.always_on_top = always_on_top; self } /// Enable or disable decorations - pub fn decorations(mut self, decorations: bool) -> Self { + pub fn set_decorations(mut self, decorations: bool) -> Self { self.decorations = decorations; self } /// Hide or show the window - pub fn visible(mut self, visible: bool) -> Self { + pub fn set_visible(mut self, visible: bool) -> Self { self.visible = visible; self } /// Mouse events pass through window - pub fn mouse_passthrough(mut self, mouse_passthrough: bool) -> Self { + pub fn set_mouse_passthrough(mut self, mouse_passthrough: bool) -> Self { self.mouse_passthrough = mouse_passthrough; self } - /// Use or create the canvas with this id. Only Web. - pub fn canvas_id(mut self, canvas_id: &str) -> Self { - self.canvas_id = canvas_id.to_string(); + /// Set the App ID + /// On web it will use or create the canvas with this id. + pub fn set_app_id(mut self, app_id: &str) -> Self { + self.app_id = app_id.to_string(); self } /// Window icon path - pub fn window_icon(mut self, window_icon_path: Option) -> Self { + pub fn set_window_icon(mut self, window_icon_path: Option) -> Self { self.window_icon_path = window_icon_path; self } @@ -234,7 +247,7 @@ impl WindowConfig { } /// Task bar icon path - pub fn taskbar_icon(mut self, taskbar_icon_path: Option) -> Self { + pub fn set_taskbar_icon(mut self, taskbar_icon_path: Option) -> Self { self.taskbar_icon_path = taskbar_icon_path; self } diff --git a/crates/notan_app/src/empty.rs b/crates/notan_app/src/empty.rs index 41a30911..e92d43db 100644 --- a/crates/notan_app/src/empty.rs +++ b/crates/notan_app/src/empty.rs @@ -1,7 +1,5 @@ use crate::config::WindowConfig; -use crate::{ - App, Backend, BackendSystem, CursorIcon, EventIterator, FrameState, InitializeFn, WindowBackend, -}; +use crate::{Backend, BackendRunner, BackendSystem, CursorIcon, EventIterator, WindowBackend}; use notan_graphics::prelude::*; use std::any::Any; @@ -17,9 +15,10 @@ use notan_audio::AudioBackend; #[derive(Default)] pub struct EmptyWindowBackend { title: String, - size: (i32, i32), + size: (u32, u32), position: (i32, i32), is_fullscreen: bool, + is_focused: bool, is_always_on_top: bool, lazy: bool, captured: bool, @@ -53,6 +52,10 @@ impl WindowBackend for EmptyWindowBackend { self.is_fullscreen } + fn is_focused(&self) -> bool { + self.is_focused + } + fn lazy_loop(&self) -> bool { self.lazy } @@ -83,6 +86,8 @@ impl WindowBackend for EmptyWindowBackend { fn set_cursor(&mut self, _cursor: CursorIcon) {} + fn set_cursor_position(&mut self, _x: f32, _y: f32) {} + fn set_fullscreen(&mut self, enabled: bool) { self.is_fullscreen = enabled; } @@ -99,7 +104,7 @@ impl WindowBackend for EmptyWindowBackend { self.position = (x, y); } - fn set_size(&mut self, width: i32, height: i32) { + fn set_size(&mut self, width: u32, height: u32) { self.size = (width, height); } @@ -107,7 +112,7 @@ impl WindowBackend for EmptyWindowBackend { self.visible = visible; } - fn size(&self) -> (i32, i32) { + fn size(&self) -> (u32, u32) { self.size } @@ -165,21 +170,27 @@ impl Backend for EmptyBackend { } } -impl BackendSystem for EmptyBackend { - fn initialize(&mut self, _config: WindowConfig) -> Result>, String> - where - S: 'static, - R: FnMut(&mut App, &mut S) -> Result + 'static, - { - Ok(Box::new(|mut app: App, mut state: S, mut cb: R| { - // This function should block with a loop or raf in the platform specific backends - // while !app.closed { - if let Err(e) = cb(&mut app, &mut state) { - log::error!("{}", e); +struct DefaultRunner; + +impl BackendRunner for DefaultRunner { + fn run( + &mut self, + app_loader: Box, + _config: WindowConfig, + ) -> Result<(), String> { + let mut runner = app_loader.load()?; + while !runner.app().closed { + if let Err(e) = runner.run() { + log::error!("{e}"); } - // } - Ok(()) - })) + } + Ok(()) + } +} + +impl BackendSystem for EmptyBackend { + fn runner(&self) -> Box { + Box::new(DefaultRunner) } fn get_graphics_backend(&self) -> Box { @@ -244,14 +255,14 @@ impl DeviceBackend for EmptyDeviceBackend { fn set_buffer_data(&mut self, _id: u64, _data: &[u8]) {} fn render(&mut self, commands: &[Commands], _target: Option) { - commands.iter().for_each(|cmd| log::info!("{:?}", cmd)); + commands.iter().for_each(|cmd| log::info!("{cmd:?}")); } fn clean(&mut self, to_clean: &[ResourceId]) { - log::info!("{:?}", to_clean); + log::info!("{to_clean:?}"); } - fn set_size(&mut self, _width: i32, _height: i32) {} + fn set_size(&mut self, _width: u32, _height: u32) {} fn set_dpi(&mut self, _scale_factor: f64) {} diff --git a/crates/notan_app/src/graphics.rs b/crates/notan_app/src/graphics.rs index 7852b961..0400cff2 100644 --- a/crates/notan_app/src/graphics.rs +++ b/crates/notan_app/src/graphics.rs @@ -47,7 +47,7 @@ impl Graphics { /// Returns the extension as mutable reference #[inline] - pub fn extension_mut(&self) -> Option> + pub fn extension_mut(&self) -> Option> where R: GfxRenderer, T: GfxExtension + 'static, @@ -57,7 +57,7 @@ impl Graphics { /// Returns the extension as reference #[inline] - pub fn extension(&self) -> Option> + pub fn extension(&self) -> Option> where R: GfxRenderer, T: GfxExtension + 'static, @@ -67,49 +67,49 @@ impl Graphics { /// Creates a Pipeline builder #[inline] - pub fn create_pipeline(&mut self) -> PipelineBuilder { + pub fn create_pipeline(&mut self) -> PipelineBuilder<'_, '_> { self.device.create_pipeline() } /// Creates a texture builder #[inline] - pub fn create_texture(&mut self) -> TextureBuilder { + pub fn create_texture(&mut self) -> TextureBuilder<'_, '_> { self.device.create_texture() } /// Creates a render texture builder #[inline] - pub fn create_render_texture(&mut self, width: i32, height: i32) -> RenderTextureBuilder { + pub fn create_render_texture(&mut self, width: u32, height: u32) -> RenderTextureBuilder<'_> { self.device.create_render_texture(width, height) } /// Creates a vertex buffer builder #[inline] - pub fn create_vertex_buffer(&mut self) -> VertexBufferBuilder { + pub fn create_vertex_buffer(&mut self) -> VertexBufferBuilder<'_> { self.device.create_vertex_buffer() } /// Creates a index buffer builder #[inline] - pub fn create_index_buffer(&mut self) -> IndexBufferBuilder { + pub fn create_index_buffer(&mut self) -> IndexBufferBuilder<'_> { self.device.create_index_buffer() } /// Creates a uniform buffer builder #[inline] - pub fn create_uniform_buffer(&mut self, slot: u32, name: &str) -> UniformBufferBuilder { + pub fn create_uniform_buffer(&mut self, slot: u32, name: &str) -> UniformBufferBuilder<'_> { self.device.create_uniform_buffer(slot, name) } /// Update the texture data #[inline] - pub fn update_texture<'a>(&'a mut self, texture: &'a mut Texture) -> TextureUpdater { + pub fn update_texture<'a>(&'a mut self, texture: &'a mut Texture) -> TextureUpdater<'a> { self.device.update_texture(texture) } /// Read pixels from a texture #[inline] - pub fn read_pixels<'a>(&'a mut self, texture: &'a Texture) -> TextureReader { + pub fn read_pixels<'a>(&'a mut self, texture: &'a Texture) -> TextureReader<'a> { self.device.read_pixels(texture) } @@ -117,7 +117,7 @@ impl Graphics { #[inline] pub fn render(&mut self, renderer: &G) { if let Err(err) = renderer.render(&mut self.device, &mut self.extensions, None) { - log::error!("{}", err); + log::error!("{err}"); panic!("{}", err); } } @@ -126,7 +126,7 @@ impl Graphics { #[inline] pub fn render_to(&mut self, target: &RenderTexture, renderer: &G) { if let Err(err) = renderer.render(&mut self.device, &mut self.extensions, Some(target)) { - log::error!("{}", err); + log::error!("{err}"); panic!("{}", err); } } @@ -151,13 +151,13 @@ impl Graphics { /// Returns the drawable size #[inline] - pub fn size(&self) -> (i32, i32) { + pub fn size(&self) -> (u32, u32) { self.device.size() } /// Sets the drawable size #[inline] - pub fn set_size(&mut self, width: i32, height: i32) { + pub fn set_size(&mut self, width: u32, height: u32) { self.device.set_size(width, height); } diff --git a/crates/notan_app/src/parsers/audio.rs b/crates/notan_app/src/parsers/audio.rs index ca47184a..6e4c684c 100644 --- a/crates/notan_app/src/parsers/audio.rs +++ b/crates/notan_app/src/parsers/audio.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "audio")] - use crate::assets::AssetLoader; use crate::App; use notan_audio::AudioSource; @@ -12,6 +10,6 @@ pub fn create_audio_parser() -> AssetLoader { fn parse_audio(id: &str, data: Vec, app: &mut App) -> Result { let source = app.audio.create_source(&data)?; - log::debug!("Asset '{}' parsed as AudioSource", id); + log::debug!("Asset '{id}' parsed as AudioSource"); Ok(source) } diff --git a/crates/notan_app/src/parsers/texture.rs b/crates/notan_app/src/parsers/texture.rs index 4cf7b58c..b614aa6c 100644 --- a/crates/notan_app/src/parsers/texture.rs +++ b/crates/notan_app/src/parsers/texture.rs @@ -10,6 +10,6 @@ pub fn create_texture_parser() -> AssetLoader { fn parse_image(id: &str, data: Vec, gfx: &mut Graphics) -> Result { let texture = gfx.create_texture().from_image(&data).build()?; - log::debug!("Asset '{}' parsed as Texture", id); + log::debug!("Asset '{id}' parsed as Texture"); Ok(texture) } diff --git a/crates/notan_app/src/plugins.rs b/crates/notan_app/src/plugins.rs index c814743c..c3bdaa79 100644 --- a/crates/notan_app/src/plugins.rs +++ b/crates/notan_app/src/plugins.rs @@ -136,11 +136,11 @@ impl Plugins { /// Remove the plugin of the type passed pub fn remove(&mut self) { - self.map.remove(&TypeId::of::()); + self.map.shift_remove(&TypeId::of::()); } /// Returns the plugin of the type passed - pub fn get(&self) -> Option> { + pub fn get(&self) -> Option> { self.map .get(&TypeId::of::())? .downcast_ref::>() @@ -148,7 +148,7 @@ impl Plugins { } /// Returns the plugin of the type passed as mutable reference - pub fn get_mut(&self) -> Option> { + pub fn get_mut(&self) -> Option> { self.map .get(&TypeId::of::())? .downcast_ref::>() @@ -237,10 +237,7 @@ impl Plugins { #[allow(unused_variables)] /// A plugin allow the user to extend or alter the application -pub trait Plugin -where - Self: Send + Sync, -{ +pub trait Plugin { /// Executed before the application loop fn init( &mut self, diff --git a/crates/notan_app/src/timer.rs b/crates/notan_app/src/timer.rs index 9c9ca7f3..6c01f6b5 100644 --- a/crates/notan_app/src/timer.rs +++ b/crates/notan_app/src/timer.rs @@ -1,13 +1,15 @@ use notan_utils::{Duration, Instant}; use std::collections::VecDeque; +/// Helper to measure and expose application's time events #[derive(Debug, Clone)] pub struct AppTimer { init_time: Instant, last_time: Option, delta: Duration, delta_seconds: f32, - time_since_init: f32, + elapsed: Duration, + elapsed_time: f32, fps_cache: VecDeque, fps: f32, } @@ -26,7 +28,8 @@ impl Default for AppTimer { last_time: None, delta: Duration::from_secs(0), delta_seconds: 0.0, - time_since_init: 0.0, + elapsed: Duration::from_secs(0), + elapsed_time: 0.0, fps_cache, fps, } @@ -45,39 +48,51 @@ impl AppTimer { self.last_time = Some(now); - let time_since_init = now - self.init_time; - self.time_since_init = time_since_init.as_secs_f32(); + self.elapsed = now - self.init_time; + self.elapsed_time = self.elapsed.as_secs_f32(); self.fps_cache.pop_front(); self.fps_cache.push_back(self.delta_seconds); self.fps = 1.0 / (self.fps_cache.iter().sum::() / self.fps_cache.len() as f32); } + /// Average frames per second (calculated using the last 60 frames) #[inline] pub fn fps(&self) -> f32 { self.fps } + /// Delta time between frames #[inline] pub fn delta(&self) -> Duration { self.delta } + /// Delta time between frames in seconds #[inline] pub fn delta_f32(&self) -> f32 { self.delta_seconds } + /// Elapsed time since application's init #[inline] - pub fn time_since_init(&self) -> f32 { - self.time_since_init + pub fn elapsed(&self) -> Duration { + self.elapsed } + /// Elapsed time since application's init in seconds + #[inline] + pub fn elapsed_f32(&self) -> f32 { + self.elapsed_time + } + + /// Application's init time #[inline] pub fn init_time(&self) -> Instant { self.init_time } + /// Last frame time #[inline] pub fn last_time(&self) -> Option { self.last_time diff --git a/crates/notan_audio/Cargo.toml b/crates/notan_audio/Cargo.toml index b897951b..0a6bb68a 100644 --- a/crates/notan_audio/Cargo.toml +++ b/crates/notan_audio/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "notan_audio" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides simple audio API for Notan" [dependencies] -parking_lot = "0.12.1" +parking_lot.workspace = true diff --git a/crates/notan_backend/Cargo.toml b/crates/notan_backend/Cargo.toml index 76ec51b3..5cb783f2 100644 --- a/crates/notan_backend/Cargo.toml +++ b/crates/notan_backend/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "notan_backend" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a default backend for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(target_arch = "wasm32")'.dependencies] -notan_web = { path = "../notan_web", version = "0.9.5" } +notan_web.workspace = true [target.'cfg(all(not(target_arch = "wasm32"), not(target_os = "ios")))'.dependencies] -notan_winit = { path = "../notan_winit", version = "0.9.5" } +notan_winit.workspace = true [target.'cfg(target_os = "ios")'.dependencies] -notan_app = { path = "../notan_app", version = "0.9.2" } +notan_app.workspace = true [features] audio = ["notan_web/audio", "notan_winit/audio"] diff --git a/crates/notan_core/Cargo.toml b/crates/notan_core/Cargo.toml index 92512ef7..d0a0044c 100644 --- a/crates/notan_core/Cargo.toml +++ b/crates/notan_core/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "notan_core" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Basic types and structs used in Notan" [dependencies] -serde = { version = "1", optional = true, features = ["serde_derive"] } +serde = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -web-sys = { version = "0.3.60", optional = true } +web-sys = { workspace = true, optional = true } [features] links = [] diff --git a/crates/notan_core/src/events.rs b/crates/notan_core/src/events.rs index 154d8c81..b19a1449 100644 --- a/crates/notan_core/src/events.rs +++ b/crates/notan_core/src/events.rs @@ -17,7 +17,7 @@ pub enum Event { WindowMove { x: i32, y: i32 }, /// Represents the current window's size after it was resized - WindowResize { width: i32, height: i32 }, + WindowResize { width: u32, height: u32 }, /// Represents a change on the screen aspect ration ScreenAspectChange { ratio: f64 }, @@ -40,6 +40,9 @@ pub enum Event { /// Mouse cursor has left the window's app MouseLeft { x: i32, y: i32 }, + /// Mouse was moved with this delta + MouseMotion { delta: (f64, f64) }, + /// Keyboard's key is down KeyDown { key: KeyCode }, diff --git a/crates/notan_core/src/keyboard.rs b/crates/notan_core/src/keyboard.rs index d5f970b3..2a5980c8 100644 --- a/crates/notan_core/src/keyboard.rs +++ b/crates/notan_core/src/keyboard.rs @@ -4,201 +4,449 @@ #[repr(u32)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum KeyCode { - /// The '1' key over the letters. - Key1, - /// The '2' key over the letters. - Key2, - /// The '3' key over the letters. - Key3, - /// The '4' key over the letters. - Key4, - /// The '5' key over the letters. - Key5, - /// The '6' key over the letters. - Key6, - /// The '7' key over the letters. - Key7, - /// The '8' key over the letters. - Key8, - /// The '9' key over the letters. - Key9, - /// The '0' key over the 'O' and 'P' keys. - Key0, - - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - - /// The Escape key, next to F1. + /// ` on a US keyboard. This is also called a backtick or grave. + /// This is the 半角/全角/漢字 + /// (hankaku/zenkaku/kanji) key on Japanese keyboards + Backquote, + /// Used for both the US \\ (on the 101-key layout) and also for the key + /// located between the " and Enter keys on row C of the 102-, + /// 104- and 106-key layouts. + /// Labeled # on a UK (102) keyboard. + Backslash, + /// [ on a US keyboard. + BracketLeft, + /// ] on a US keyboard. + BracketRight, + /// , on a US keyboard. + Comma, + /// 0 on a US keyboard. + Digit0, + /// 1 on a US keyboard. + Digit1, + /// 2 on a US keyboard. + Digit2, + /// 3 on a US keyboard. + Digit3, + /// 4 on a US keyboard. + Digit4, + /// 5 on a US keyboard. + Digit5, + /// 6 on a US keyboard. + Digit6, + /// 7 on a US keyboard. + Digit7, + /// 8 on a US keyboard. + Digit8, + /// 9 on a US keyboard. + Digit9, + /// = on a US keyboard. + Equal, + /// Located between the left Shift and Z keys. + /// Labeled \\ on a UK keyboard. + IntlBackslash, + /// Located between the / and right Shift keys. + /// Labeled \\ (ro) on a Japanese keyboard. + IntlRo, + /// Located between the = and Backspace keys. + /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a + /// Russian keyboard. + IntlYen, + /// a on a US keyboard. + /// Labeled q on an AZERTY (e.g., French) keyboard. + KeyA, + /// b on a US keyboard. + KeyB, + /// c on a US keyboard. + KeyC, + /// d on a US keyboard. + KeyD, + /// e on a US keyboard. + KeyE, + /// f on a US keyboard. + KeyF, + /// g on a US keyboard. + KeyG, + /// h on a US keyboard. + KeyH, + /// i on a US keyboard. + KeyI, + /// j on a US keyboard. + KeyJ, + /// k on a US keyboard. + KeyK, + /// l on a US keyboard. + KeyL, + /// m on a US keyboard. + KeyM, + /// n on a US keyboard. + KeyN, + /// o on a US keyboard. + KeyO, + /// p on a US keyboard. + KeyP, + /// q on a US keyboard. + /// Labeled a on an AZERTY (e.g., French) keyboard. + KeyQ, + /// r on a US keyboard. + KeyR, + /// s on a US keyboard. + KeyS, + /// t on a US keyboard. + KeyT, + /// u on a US keyboard. + KeyU, + /// v on a US keyboard. + KeyV, + /// w on a US keyboard. + /// Labeled z on an AZERTY (e.g., French) keyboard. + KeyW, + /// x on a US keyboard. + KeyX, + /// y on a US keyboard. + /// Labeled z on a QWERTZ (e.g., German) keyboard. + KeyY, + /// z on a US keyboard. + /// Labeled w on an AZERTY (e.g., French) keyboard, and y on a + /// QWERTZ (e.g., German) keyboard. + KeyZ, + /// - on a US keyboard. + Minus, + /// . on a US keyboard. + Period, + /// ' on a US keyboard. + Quote, + /// ; on a US keyboard. + Semicolon, + /// / on a US keyboard. + Slash, + /// Alt, Option, or . + AltLeft, + /// Alt, Option, or . + /// This is labeled AltGr on many keyboard layouts. + AltRight, + /// Backspace or . + /// Labeled Delete on Apple keyboards. + Backspace, + /// CapsLock or + CapsLock, + /// The application context menu key, which is typically found between the right + /// Super key and the right Control key. + ContextMenu, + /// Control or + ControlLeft, + /// Control or + ControlRight, + /// Enter or . Labeled Return on Apple keyboards. + Enter, + /// The Windows, , Command, or other OS symbol key. + SuperLeft, + /// The Windows, , Command, or other OS symbol key. + SuperRight, + /// Shift or + ShiftLeft, + /// Shift or + ShiftRight, + /// (space) + Space, + /// Tab or + Tab, + /// Japanese: (henkan) + Convert, + /// Japanese: カタカナ/ひらがな/ローマ字 + /// (katakana/hiragana/romaji) + KanaMode, + /// Korean: HangulMode 한/영 (han/yeong) + /// + /// Japanese (Mac keyboard): (kana) + Lang1, + /// Korean: Hanja (hanja) + /// + /// Japanese (Mac keyboard): (eisu) + Lang2, + /// Japanese (word-processing keyboard): Katakana + Lang3, + /// Japanese (word-processing keyboard): Hiragana + Lang4, + /// Japanese (word-processing keyboard): Zenkaku/Hankaku + Lang5, + /// Japanese: 無変換 (muhenkan) + NonConvert, + /// . The forward delete key. + /// Note that on Apple keyboards, the key labelled Delete on the main part of + /// the keyboard is encoded as [`Backspace`]. + /// + /// [`Backspace`]: Self::Backspace + Delete, + /// Page Down, End, or + End, + /// Help. Not present on standard PC keyboards. + Help, + /// Home or + Home, + /// Insert or Ins. Not present on Apple keyboards. + Insert, + /// Page Down, PgDn, or + PageDown, + /// Page Up, PgUp, or + PageUp, + /// + ArrowDown, + /// + ArrowLeft, + /// + ArrowRight, + /// + ArrowUp, + /// On the Mac, this is used for the numpad Clear key. + NumLock, + /// 0 Ins on a keyboard. 0 on a phone or remote control + Numpad0, + /// 1 End on a keyboard. 1 or 1 QZ on a phone or remote + /// control + Numpad1, + /// 2 ↓ on a keyboard. 2 ABC on a phone or remote control + Numpad2, + /// 3 PgDn on a keyboard. 3 DEF on a phone or remote control + Numpad3, + /// 4 ← on a keyboard. 4 GHI on a phone or remote control + Numpad4, + /// 5 on a keyboard. 5 JKL on a phone or remote control + Numpad5, + /// 6 → on a keyboard. 6 MNO on a phone or remote control + Numpad6, + /// 7 Home on a keyboard. 7 PQRS or 7 PRS on a phone + /// or remote control + Numpad7, + /// 8 ↑ on a keyboard. 8 TUV on a phone or remote control + Numpad8, + /// 9 PgUp on a keyboard. 9 WXYZ or 9 WXY on a phone + /// or remote control + Numpad9, + /// + + NumpadAdd, + /// Found on the Microsoft Natural Keyboard. + NumpadBackspace, + /// C or A (All Clear). Also for use with numpads that have a + /// Clear key that is separate from the NumLock key. On the Mac, the + /// numpad Clear key is encoded as [`NumLock`]. + /// + /// [`NumLock`]: Self::NumLock + NumpadClear, + /// C (Clear Entry) + NumpadClearEntry, + /// , (thousands separator). For locales where the thousands separator + /// is a "." (e.g., Brazil), this key may generate a .. + NumpadComma, + /// . Del. For locales where the decimal separator is "," (e.g., + /// Brazil), this key may generate a ,. + NumpadDecimal, + /// / + NumpadDivide, + NumpadEnter, + /// = + NumpadEqual, + /// # on a phone or remote control device. This key is typically found + /// below the 9 key and to the right of the 0 key. + NumpadHash, + /// M Add current entry to the value stored in memory. + NumpadMemoryAdd, + /// M Clear the value stored in memory. + NumpadMemoryClear, + /// M Replace the current entry with the value stored in memory. + NumpadMemoryRecall, + /// M Replace the value stored in memory with the current entry. + NumpadMemoryStore, + /// M Subtract current entry from the value stored in memory. + NumpadMemorySubtract, + /// * on a keyboard. For use with numpads that provide mathematical + /// operations (+, - * and /). + /// + /// Use `NumpadStar` for the * key on phones and remote controls. + NumpadMultiply, + /// ( Found on the Microsoft Natural Keyboard. + NumpadParenLeft, + /// ) Found on the Microsoft Natural Keyboard. + NumpadParenRight, + /// * on a phone or remote control device. + /// + /// This key is typically found below the 7 key and to the left of + /// the 0 key. + /// + /// Use "NumpadMultiply" for the * key on + /// numeric keypads. + NumpadStar, + /// - + NumpadSubtract, + /// Esc or Escape, - + /// Fn This is typically a hardware key that does not generate a separate code. + Fn, + /// FLock or FnLock. Function Lock key. Found on the Microsoft + /// Natural Keyboard. + FnLock, + /// PrtScr SysRq or Print Screen + PrintScreen, + /// Scroll Lock + ScrollLock, + /// Pause Break + Pause, + /// Some laptops place this key to the left of the key. + /// + /// This also the "back" button (triangle) on Android. + BrowserBack, + BrowserFavorites, + /// Some laptops place this key to the right of the key. + BrowserForward, + /// The "home" button on Android. + BrowserHome, + BrowserRefresh, + BrowserSearch, + BrowserStop, + /// Eject or . This key is placed in the function section on some Apple + /// keyboards. + Eject, + /// Sometimes labelled My Computer on the keyboard + LaunchApp1, + /// Sometimes labelled Calculator on the keyboard + LaunchApp2, + LaunchMail, + MediaPlayPause, + MediaSelect, + MediaStop, + MediaTrackNext, + MediaTrackPrevious, + /// This key is placed in the function section on some Apple keyboards, replacing the + /// Eject key. + Power, + Sleep, + AudioVolumeDown, + AudioVolumeMute, + AudioVolumeUp, + WakeUp, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. + Hyper, + Turbo, + Abort, + Resume, + Suspend, + /// Found on Sun’s USB keyboard. + Again, + /// Found on Sun’s USB keyboard. + Copy, + /// Found on Sun’s USB keyboard. + Cut, + /// Found on Sun’s USB keyboard. + Find, + /// Found on Sun’s USB keyboard. + Open, + /// Found on Sun’s USB keyboard. + Paste, + /// Found on Sun’s USB keyboard. + Props, + /// Found on Sun’s USB keyboard. + Select, + /// Found on Sun’s USB keyboard. + Undo, + /// Use for dedicated ひらがな key found on some Japanese word processing keyboards. + Hiragana, + /// Use for dedicated カタカナ key found on some Japanese word processing keyboards. + Katakana, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. F24, - - /// Print Screen/SysRq. - Snapshot, - /// Scroll Lock. - Scroll, - /// Pause/Break key, next to Scroll lock. - Pause, - - /// `Insert`, next to Backspace. - Insert, - Home, - Delete, - End, - PageDown, - PageUp, - - Left, - Up, - Right, - Down, - - /// The Backspace key, right over Enter. - Back, - /// The Enter key. - Return, - /// The space bar. - Space, - - /// The "Compose" key on Linux. - Compose, - - Caret, - - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - Add, - Divide, - Decimal, - NumpadComma, - NumpadEnter, - NumpadEquals, - Multiply, - Subtract, - - AbntC1, - AbntC2, - Apostrophe, - Apps, - Asterisk, - At, - Ax, - Backslash, - Calculator, - Capital, - Colon, - Comma, - Convert, - Equals, - Grave, - Kana, - Kanji, - LAlt, - LBracket, - LControl, - LShift, - LWin, - Mail, - MediaSelect, - MediaStop, - Minus, - Mute, - MyComputer, - // also called "Next" - NavigateForward, - // also called "Prior" - NavigateBackward, - NextTrack, - NoConvert, - OEM102, - Period, - PlayPause, - Plus, - Power, - PrevTrack, - RAlt, - RBracket, - RControl, - RShift, - RWin, - Semicolon, - Slash, - Sleep, - Stop, - Sysrq, - Tab, - Underline, - Unlabeled, - VolumeDown, - VolumeUp, - Wake, - WebBack, - WebFavorites, - WebForward, - WebHome, - WebRefresh, - WebSearch, - WebStop, - Yen, - Copy, - Paste, - Cut, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, Unknown, } diff --git a/crates/notan_core/src/mouse.rs b/crates/notan_core/src/mouse.rs index c6fc82a0..a727556d 100644 --- a/crates/notan_core/src/mouse.rs +++ b/crates/notan_core/src/mouse.rs @@ -5,5 +5,7 @@ pub enum MouseButton { Left, Right, Middle, - Other(u8), + Back, + Forward, + Other(u16), } diff --git a/crates/notan_draw/Cargo.toml b/crates/notan_draw/Cargo.toml index d597d54c..31aa43e8 100644 --- a/crates/notan_draw/Cargo.toml +++ b/crates/notan_draw/Cargo.toml @@ -1,24 +1,33 @@ [package] name = "notan_draw" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a simple 2D API for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -notan_app = { path = "../notan_app", version = "0.9.5" } -notan_graphics = { path = "../notan_graphics", version = "0.9.5" } -notan_macro = { path = "../notan_macro", version = "0.9.5" } -notan_math = { path = "../notan_math", version = "0.9.5" } -notan_glyph = { path = "../notan_glyph", version = "0.9.5" } -notan_text = { path = "../notan_text", version = "0.9.5" } +log.workspace = true +notan_app.workspace = true +notan_graphics.workspace = true +notan_macro.workspace = true +notan_math.workspace = true +notan_glyph.workspace = true +notan_text.workspace = true +serde = { workspace = true, features = ["derive"] } + lyon = "1.0.1" -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.91" +serde_json = "1.0.138" + +[features] +glsl-to-spirv = [ + "notan_macro/glsl-to-spirv", + "notan_glyph/glsl-to-spirv", + "notan_text/glsl-to-spirv", +] +shaderc = ["notan_macro/shaderc", "notan_glyph/shaderc", "notan_text/shaderc"] diff --git a/crates/notan_draw/src/atlas.rs b/crates/notan_draw/src/atlas.rs index 32769658..21126842 100644 --- a/crates/notan_draw/src/atlas.rs +++ b/crates/notan_draw/src/atlas.rs @@ -44,6 +44,7 @@ struct AtlasFrame { sprite_source_size: AtlasRect, #[serde(alias = "sourceSize")] source_size: AtlasSize, + #[serde(default)] pivot: AtlasPoint, } @@ -63,6 +64,12 @@ struct AtlasPoint { y: f32, } +impl Default for AtlasPoint { + fn default() -> Self { + Self { x: 0.5, y: 0.5 } + } +} + #[derive(Serialize, Deserialize, Debug)] struct AtlasSize { w: i32, diff --git a/crates/notan_draw/src/builder.rs b/crates/notan_draw/src/builder.rs index 8f1bd32e..fd29ace0 100644 --- a/crates/notan_draw/src/builder.rs +++ b/crates/notan_draw/src/builder.rs @@ -59,7 +59,7 @@ where } } -impl<'a, T> DrawBuilder<'a, T> +impl DrawBuilder<'_, T> where T: DrawProcess + DrawTransform, { diff --git a/crates/notan_draw/src/custom_pipeline.rs b/crates/notan_draw/src/custom_pipeline.rs index 7885aaf5..3619b55f 100644 --- a/crates/notan_draw/src/custom_pipeline.rs +++ b/crates/notan_draw/src/custom_pipeline.rs @@ -20,27 +20,29 @@ impl std::cmp::PartialEq for CustomPipeline { } } +#[allow(mismatched_lifetime_syntaxes)] pub trait DrawCustomPipeline { - fn image_pipeline(&mut self) -> CustomPipelineBuilder; - fn shape_pipeline(&mut self) -> CustomPipelineBuilder; - fn pattern_pipeline(&mut self) -> CustomPipelineBuilder; - fn text_pipeline(&mut self) -> CustomPipelineBuilder; + fn image_pipeline(&mut self) -> CustomPipelineBuilder<'_>; + fn shape_pipeline(&mut self) -> CustomPipelineBuilder<'_>; + fn pattern_pipeline(&mut self) -> CustomPipelineBuilder<'_>; + fn text_pipeline(&mut self) -> CustomPipelineBuilder<'_>; } +#[allow(mismatched_lifetime_syntaxes)] impl DrawCustomPipeline for Draw { - fn image_pipeline(&mut self) -> CustomPipelineBuilder { + fn image_pipeline(&mut self) -> CustomPipelineBuilder<'_> { CustomPipelineBuilder::new(self, CustomPipelineType::Image) } - fn shape_pipeline(&mut self) -> CustomPipelineBuilder { + fn shape_pipeline(&mut self) -> CustomPipelineBuilder<'_> { CustomPipelineBuilder::new(self, CustomPipelineType::Shape) } - fn pattern_pipeline(&mut self) -> CustomPipelineBuilder { + fn pattern_pipeline(&mut self) -> CustomPipelineBuilder<'_> { CustomPipelineBuilder::new(self, CustomPipelineType::Pattern) } - fn text_pipeline(&mut self) -> CustomPipelineBuilder { + fn text_pipeline(&mut self) -> CustomPipelineBuilder<'_> { CustomPipelineBuilder::new(self, CustomPipelineType::Text) } } diff --git a/crates/notan_draw/src/draw.rs b/crates/notan_draw/src/draw.rs index 117033bb..40fddbcb 100644 --- a/crates/notan_draw/src/draw.rs +++ b/crates/notan_draw/src/draw.rs @@ -60,7 +60,7 @@ impl Clone for Draw { } impl Draw { - pub fn new(width: i32, height: i32) -> Self { + pub fn new(width: u32, height: u32) -> Self { let base_projection = Mat4::orthographic_rh_gl(0.0, width as _, height as _, 0.0, -1.0, 1.0); @@ -319,7 +319,7 @@ impl Draw { if let BatchType::Text { texts } = &mut b.typ { let global_matrix = *self.transform.matrix(); let matrix = match *info.transform() { - Some(m) => *m * global_matrix, + Some(m) => global_matrix * *m, _ => global_matrix, }; diff --git a/crates/notan_draw/src/images.rs b/crates/notan_draw/src/images.rs index 31d234ed..079f2261 100644 --- a/crates/notan_draw/src/images.rs +++ b/crates/notan_draw/src/images.rs @@ -14,25 +14,30 @@ use notan_graphics::Texture; pub use painter::create_image_pipeline; pub(crate) use painter::*; +#[allow(mismatched_lifetime_syntaxes)] pub trait DrawImages { - fn image<'a>(&mut self, texture: &'a Texture) -> DrawBuilder>; - fn nine_slice<'a>(&mut self, texture: &'a Texture) -> DrawBuilder>; + fn image<'a>(&mut self, texture: &'a Texture) -> DrawBuilder<'_, Image<'a>>; + fn nine_slice<'a>(&mut self, texture: &'a Texture) -> DrawBuilder<'_, NineSlice<'a>>; fn animation_grid<'a>( &mut self, texture: &'a Texture, cols: usize, rows: usize, - ) -> DrawBuilder>; - fn animation_list<'a>(&mut self, list: &'a [&'a Texture]) -> DrawBuilder>; + ) -> DrawBuilder<'_, ImageAnimation<'a>>; + fn animation_list<'a>( + &mut self, + list: &'a [&'a Texture], + ) -> DrawBuilder<'_, ImageAnimation<'a>>; //fn instanced_image<'a>(&mut self, texture: &'a Texture) -> DrawBuilder>; } +#[allow(mismatched_lifetime_syntaxes)] impl DrawImages for Draw { - fn image<'a>(&mut self, texture: &'a Texture) -> DrawBuilder> { + fn image<'a>(&mut self, texture: &'a Texture) -> DrawBuilder<'_, Image<'a>> { DrawBuilder::new(self, Image::new(texture)) } - fn nine_slice<'a>(&mut self, texture: &'a Texture) -> DrawBuilder> { + fn nine_slice<'a>(&mut self, texture: &'a Texture) -> DrawBuilder<'_, NineSlice<'a>> { DrawBuilder::new(self, NineSlice::new(texture)) } @@ -41,11 +46,14 @@ impl DrawImages for Draw { texture: &'a Texture, cols: usize, rows: usize, - ) -> DrawBuilder> { + ) -> DrawBuilder<'_, ImageAnimation<'a>> { DrawBuilder::new(self, ImageAnimation::from_grid(texture, cols, rows)) } - fn animation_list<'a>(&mut self, list: &'a [&'a Texture]) -> DrawBuilder> { + fn animation_list<'a>( + &mut self, + list: &'a [&'a Texture], + ) -> DrawBuilder<'_, ImageAnimation<'a>> { DrawBuilder::new(self, ImageAnimation::from_list(list)) } } diff --git a/crates/notan_draw/src/lib.rs b/crates/notan_draw/src/lib.rs index 27487faf..99f5dd6b 100644 --- a/crates/notan_draw/src/lib.rs +++ b/crates/notan_draw/src/lib.rs @@ -14,6 +14,7 @@ mod transform; mod atlas; pub use atlas::*; +pub use builder::*; pub use config::*; pub use custom_pipeline::*; pub use draw::*; diff --git a/crates/notan_draw/src/manager.rs b/crates/notan_draw/src/manager.rs index 12664743..cb3a1651 100644 --- a/crates/notan_draw/src/manager.rs +++ b/crates/notan_draw/src/manager.rs @@ -46,7 +46,7 @@ impl DrawManager { self.renderer.commands() } - pub fn create_draw(&self, width: i32, height: i32) -> Draw { + pub fn create_draw(&self, width: u32, height: u32) -> Draw { Draw::new(width, height) } @@ -129,7 +129,7 @@ fn process_glyphs( ) { if let Some(indices) = &draw.text_batch_indices { let batch_len = draw.batches.len(); - let mut last_index = std::usize::MAX; + let mut last_index = usize::MAX; indices.iter().for_each(|i| { let n = *i; if n == last_index { @@ -171,7 +171,7 @@ fn process_draw( manager.text_painter.clear(); let stencil = draw.needs_to_clean_stencil.then_some(0x00); - manager.renderer.begin(Some(&ClearOptions { + manager.renderer.begin(Some(ClearOptions { color: draw.clear_color, stencil, ..Default::default() @@ -259,11 +259,11 @@ fn blended_pip( pip: &Pipeline, blend_mode: Option, alpha_mode: Option, - is_rt: bool, + _is_rt: bool, ) -> Option { // commented the following code because blank frames were shown // drawing to a rt needs over mode - // let alpha_mode = alpha_mode.or(if is_rt { Some(BlendMode::OVER) } else { None }); + // let alpha_mode = alpha_mode.or(if _is_rt { Some(BlendMode::OVER) } else { None }); let new_cbm = pip.options.color_blend != blend_mode; let new_abm = pip.options.alpha_blend != alpha_mode; if new_cbm || new_abm { diff --git a/crates/notan_draw/src/patterns.rs b/crates/notan_draw/src/patterns.rs index 5fd65d14..c07410ef 100644 --- a/crates/notan_draw/src/patterns.rs +++ b/crates/notan_draw/src/patterns.rs @@ -8,12 +8,14 @@ pub use painter::create_pattern_pipeline; pub(crate) use painter::*; pub use pattern::*; +#[allow(mismatched_lifetime_syntaxes)] pub trait DrawPattern { - fn pattern<'a>(&mut self, texture: &'a Texture) -> DrawBuilder>; + fn pattern<'a>(&mut self, texture: &'a Texture) -> DrawBuilder<'_, Pattern<'a>>; } +#[allow(mismatched_lifetime_syntaxes)] impl DrawPattern for Draw { - fn pattern<'a>(&mut self, texture: &'a Texture) -> DrawBuilder> { + fn pattern<'a>(&mut self, texture: &'a Texture) -> DrawBuilder<'_, Pattern<'a>> { DrawBuilder::new(self, Pattern::new(texture)) } } diff --git a/crates/notan_draw/src/shapes.rs b/crates/notan_draw/src/shapes.rs index a13ca769..92a46be5 100644 --- a/crates/notan_draw/src/shapes.rs +++ b/crates/notan_draw/src/shapes.rs @@ -4,6 +4,7 @@ mod geometry; mod line; mod painter; mod path; +mod point; mod polygon; mod rect; mod star; @@ -18,52 +19,70 @@ pub use line::Line; pub use painter::create_shape_pipeline; pub(crate) use painter::*; pub use path::Path; +pub use point::{Point, XAlignment, YAlignment}; pub use polygon::Polygon; pub use rect::Rectangle; pub use star::Star; pub use triangle::Triangle; +#[allow(mismatched_lifetime_syntaxes)] pub trait DrawShapes { - fn line(&mut self, p1: (f32, f32), p2: (f32, f32)) -> DrawBuilder; - fn triangle(&mut self, a: (f32, f32), b: (f32, f32), c: (f32, f32)) -> DrawBuilder; - fn path(&mut self) -> DrawBuilder; - fn rect(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder; - fn circle(&mut self, radius: f32) -> DrawBuilder; - fn ellipse(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder; - fn star(&mut self, spikes: u8, outer_radius: f32, inner_radius: f32) -> DrawBuilder; - fn polygon(&mut self, sides: u8, radius: f32) -> DrawBuilder; + fn point(&mut self, x: f32, y: f32) -> DrawBuilder<'_, Point>; + fn line(&mut self, p1: (f32, f32), p2: (f32, f32)) -> DrawBuilder<'_, Line>; + fn triangle( + &mut self, + a: (f32, f32), + b: (f32, f32), + c: (f32, f32), + ) -> DrawBuilder<'_, Triangle>; + fn path(&mut self) -> DrawBuilder<'_, Path>; + fn rect(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder<'_, Rectangle>; + fn circle(&mut self, radius: f32) -> DrawBuilder<'_, Circle>; + fn ellipse(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder<'_, Ellipse>; + fn star(&mut self, spikes: u8, outer_radius: f32, inner_radius: f32) -> DrawBuilder<'_, Star>; + fn polygon(&mut self, sides: u8, radius: f32) -> DrawBuilder<'_, Polygon>; } +#[allow(mismatched_lifetime_syntaxes)] impl DrawShapes for Draw { - fn line(&mut self, p1: (f32, f32), p2: (f32, f32)) -> DrawBuilder { + fn point(&mut self, x: f32, y: f32) -> DrawBuilder<'_, Point> { + DrawBuilder::new(self, Point::new(x, y)) + } + + fn line(&mut self, p1: (f32, f32), p2: (f32, f32)) -> DrawBuilder<'_, Line> { DrawBuilder::new(self, Line::new(p1, p2)) } - fn triangle(&mut self, a: (f32, f32), b: (f32, f32), c: (f32, f32)) -> DrawBuilder { + fn triangle( + &mut self, + a: (f32, f32), + b: (f32, f32), + c: (f32, f32), + ) -> DrawBuilder<'_, Triangle> { DrawBuilder::new(self, Triangle::new(a, b, c)) } - fn path(&mut self) -> DrawBuilder { + fn path(&mut self) -> DrawBuilder<'_, Path> { DrawBuilder::new(self, Path::new()) } - fn rect(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder { + fn rect(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder<'_, Rectangle> { DrawBuilder::new(self, Rectangle::new(position, size)) } - fn circle(&mut self, radius: f32) -> DrawBuilder { + fn circle(&mut self, radius: f32) -> DrawBuilder<'_, Circle> { DrawBuilder::new(self, Circle::new(radius)) } - fn ellipse(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder { + fn ellipse(&mut self, position: (f32, f32), size: (f32, f32)) -> DrawBuilder<'_, Ellipse> { DrawBuilder::new(self, Ellipse::new(position, size)) } - fn star(&mut self, spikes: u8, outer_radius: f32, inner_radius: f32) -> DrawBuilder { + fn star(&mut self, spikes: u8, outer_radius: f32, inner_radius: f32) -> DrawBuilder<'_, Star> { DrawBuilder::new(self, Star::new(spikes, outer_radius, inner_radius)) } - fn polygon(&mut self, sides: u8, radius: f32) -> DrawBuilder { + fn polygon(&mut self, sides: u8, radius: f32) -> DrawBuilder<'_, Polygon> { DrawBuilder::new(self, Polygon::new(sides, radius)) } } diff --git a/crates/notan_draw/src/shapes/point.rs b/crates/notan_draw/src/shapes/point.rs new file mode 100644 index 00000000..97d0bb68 --- /dev/null +++ b/crates/notan_draw/src/shapes/point.rs @@ -0,0 +1,163 @@ +use super::path::Path; +use crate::builder::DrawProcess; +use crate::draw::Draw; +use crate::transform::DrawTransform; +use notan_graphics::color::Color; +use notan_math::Mat3; + +/// Point alignment in the X axis. +/// +/// This is only meaningful for width > 1. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub enum XAlignment { + Left, + #[default] + Center, + Right, +} +/// Point alignment in the Y axis. +/// +/// This is only meaningful for width > 1. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub enum YAlignment { + Top, + #[default] + Center, + Bottom, +} + +/// A single point. +pub struct Point { + x: f32, + y: f32, + color: Color, + stroke_width: f32, + x_align: XAlignment, + y_align: YAlignment, + alpha: f32, + matrix: Option, +} + +impl Point { + pub fn new(x: f32, y: f32) -> Self { + Self { + x, + y, + color: Color::WHITE, + stroke_width: 1.0, + x_align: XAlignment::Center, + y_align: YAlignment::Center, + alpha: 1.0, + matrix: None, + } + } + + pub fn color(&mut self, color: Color) -> &mut Self { + self.color = color; + self + } + + pub fn width(&mut self, width: f32) -> &mut Self { + self.stroke_width = width; + self + } + + pub fn alpha(&mut self, alpha: f32) -> &mut Self { + self.alpha = alpha; + self + } + + pub fn align(&mut self, x_align: XAlignment, y_align: YAlignment) -> &mut Self { + self.x_align = x_align; + self.y_align = y_align; + self + } + pub fn x_align(&mut self, x_align: XAlignment) -> &mut Self { + self.x_align = x_align; + self + } + pub fn y_align(&mut self, y_align: YAlignment) -> &mut Self { + self.y_align = y_align; + self + } + pub fn x_align_left(&mut self) -> &mut Self { + self.x_align = XAlignment::Left; + self + } + pub fn x_align_center(&mut self) -> &mut Self { + self.x_align = XAlignment::Center; + self + } + pub fn x_align_right(&mut self) -> &mut Self { + self.x_align = XAlignment::Right; + self + } + pub fn y_align_top(&mut self) -> &mut Self { + self.y_align = YAlignment::Top; + self + } + pub fn y_align_middle(&mut self) -> &mut Self { + self.y_align = YAlignment::Center; + self + } + pub fn y_align_bottom(&mut self) -> &mut Self { + self.y_align = YAlignment::Bottom; + self + } +} + +impl DrawTransform for Point { + fn matrix(&mut self) -> &mut Option { + &mut self.matrix + } +} + +impl DrawProcess for Point { + fn draw_process(self, draw: &mut Draw) { + let Self { + x, + y, + color, + stroke_width, + x_align, + y_align, + alpha, + matrix, + } = self; + + let mut path = Path::new(); + + let x_from = match x_align { + XAlignment::Left => x + stroke_width / 2., + XAlignment::Center => x + 1.0, + XAlignment::Right => x + 1.0 - stroke_width / 2., + }; + let x_to = match x_align { + XAlignment::Left => x + stroke_width / 2., + XAlignment::Center => x + 1.0, + XAlignment::Right => x + 1.0 - stroke_width / 2., + }; + let y_from = match y_align { + YAlignment::Top => y, + YAlignment::Center => y + 1.0 - stroke_width / 2., + YAlignment::Bottom => y + 1.0 - stroke_width, + }; + let y_to = match y_align { + YAlignment::Top => y + stroke_width, + YAlignment::Center => y + 1.0 + stroke_width / 2., + YAlignment::Bottom => y + 1.0, + }; + + path.move_to(x_from, y_from) + .line_to(x_to, y_to) + .stroke(stroke_width) + .color(color.with_alpha(color.a * alpha)) + .close(); + + if let Some(m) = matrix { + path.transform(m); + } + + path.draw_process(draw); + } +} diff --git a/crates/notan_draw/src/shapes/polygon.rs b/crates/notan_draw/src/shapes/polygon.rs index 5de114a7..d1d53a67 100644 --- a/crates/notan_draw/src/shapes/polygon.rs +++ b/crates/notan_draw/src/shapes/polygon.rs @@ -153,7 +153,7 @@ fn draw_polygon( let i = n as f32; let pi_sides = PI / sides as f32; - let is_even = sides % 2 == 0; + let is_even = sides.is_multiple_of(2); let offset = if is_even { pi_sides } else { pi_sides * 0.5 }; let angle = i * 2.0 * pi_sides - offset; diff --git a/crates/notan_draw/src/shapes/rect.rs b/crates/notan_draw/src/shapes/rect.rs index a4994016..1e4f0321 100644 --- a/crates/notan_draw/src/shapes/rect.rs +++ b/crates/notan_draw/src/shapes/rect.rs @@ -26,6 +26,7 @@ pub struct Rectangle { stroke_color: Option, } +#[allow(unused_assignments)] impl Rectangle { pub fn new(position: (f32, f32), size: (f32, f32)) -> Self { Self { @@ -59,24 +60,28 @@ impl Rectangle { pub fn top_left_radius(&mut self, radius: f32) -> &mut Self { let mut corners = self.rounded_corners.unwrap_or([0.0, 0.0, 0.0, 0.0]); corners[0] = radius; + self.rounded_corners = Some(corners); self } pub fn top_right_radius(&mut self, radius: f32) -> &mut Self { let mut corners = self.rounded_corners.unwrap_or([0.0, 0.0, 0.0, 0.0]); corners[1] = radius; + self.rounded_corners = Some(corners); self } pub fn bottom_left_radius(&mut self, radius: f32) -> &mut Self { let mut corners = self.rounded_corners.unwrap_or([0.0, 0.0, 0.0, 0.0]); corners[2] = radius; + self.rounded_corners = Some(corners); self } pub fn bottom_right_radius(&mut self, radius: f32) -> &mut Self { let mut corners = self.rounded_corners.unwrap_or([0.0, 0.0, 0.0, 0.0]); corners[3] = radius; + self.rounded_corners = Some(corners); self } diff --git a/crates/notan_draw/src/texts.rs b/crates/notan_draw/src/texts.rs index 88e72225..2e9fb275 100644 --- a/crates/notan_draw/src/texts.rs +++ b/crates/notan_draw/src/texts.rs @@ -8,12 +8,14 @@ pub use painter::create_text_pipeline; pub(crate) use painter::*; pub use text::*; +#[allow(mismatched_lifetime_syntaxes)] pub trait DrawTextSection { - fn text<'a>(&mut self, font: &'a Font, text: &'a str) -> DrawBuilder>; + fn text<'a>(&mut self, font: &'a Font, text: &'a str) -> DrawBuilder<'_, TextSection<'a>>; } +#[allow(mismatched_lifetime_syntaxes)] impl DrawTextSection for Draw { - fn text<'a>(&mut self, font: &'a Font, text: &'a str) -> DrawBuilder> { + fn text<'a>(&mut self, font: &'a Font, text: &'a str) -> DrawBuilder<'_, TextSection<'a>> { DrawBuilder::new(self, TextSection::new(font, text)) } } diff --git a/crates/notan_draw/src/texts/painter.rs b/crates/notan_draw/src/texts/painter.rs index 775d2aed..82557ce8 100644 --- a/crates/notan_draw/src/texts/painter.rs +++ b/crates/notan_draw/src/texts/painter.rs @@ -223,7 +223,7 @@ impl GlyphPipeline for TextPainter { texture: &Texture, _clear: Option, _transform: Mat4, - _size: (i32, i32), + _size: (u32, u32), _region: Option, ) { renderer.bind_texture_slot(0, 0, texture); diff --git a/crates/notan_draw/src/transform.rs b/crates/notan_draw/src/transform.rs index 746980c3..42d7507b 100644 --- a/crates/notan_draw/src/transform.rs +++ b/crates/notan_draw/src/transform.rs @@ -22,7 +22,7 @@ pub trait DrawTransform { /// Set the matrix position fn translate(&mut self, x: f32, y: f32) -> &mut Self { let old = self.matrix().unwrap_or_else(|| Mat3::IDENTITY); - let matrix = old * Mat3::from_translation(Vec2::new(x, y)); + let matrix = Mat3::from_translation(Vec2::new(x, y)) * old; *self.matrix() = Some(matrix); self } @@ -30,7 +30,7 @@ pub trait DrawTransform { /// Set the matrix scale fn scale(&mut self, x: f32, y: f32) -> &mut Self { let old = self.matrix().unwrap_or_else(|| Mat3::IDENTITY); - let matrix = old * Mat3::from_scale(Vec2::new(x, y)); + let matrix = Mat3::from_scale(Vec2::new(x, y)) * old; *self.matrix() = Some(matrix); self } @@ -38,7 +38,7 @@ pub trait DrawTransform { /// Set the matrix rotation using radians fn rotate(&mut self, angle: f32) -> &mut Self { let old = self.matrix().unwrap_or_else(|| Mat3::IDENTITY); - let matrix = old * Mat3::from_angle(angle); + let matrix = Mat3::from_angle(angle) * old; *self.matrix() = Some(matrix); self } @@ -62,16 +62,18 @@ pub trait DrawTransform { Vec3::new(0.0, 0.0, 1.0), ); - *self.matrix() = Some(old * new); + *self.matrix() = Some(new * old); self } /// Set the matrix rotation using radians from the point given fn rotate_from(&mut self, point: (f32, f32), angle: f32) -> &mut Self { let old = self.matrix().unwrap_or_else(|| Mat3::IDENTITY); - let translate = old * Mat3::from_translation(Vec2::new(point.0, point.1)); - let rotate = translate * Mat3::from_angle(angle); - let matrix = rotate * Mat3::from_translation(Vec2::new(-point.0, -point.1)); + let translate_to_origin = Mat3::from_translation(Vec2::new(-point.0, -point.1)); + let rotate = Mat3::from_angle(angle); + let translate_back = Mat3::from_translation(Vec2::new(point.0, point.1)); + + let matrix = translate_back * rotate * translate_to_origin * old; *self.matrix() = Some(matrix); self } @@ -85,9 +87,11 @@ pub trait DrawTransform { /// Set the matrix scale from the point given fn scale_from(&mut self, point: (f32, f32), scale: (f32, f32)) -> &mut Self { let old = self.matrix().unwrap_or_else(|| Mat3::IDENTITY); - let translate = old * Mat3::from_translation(Vec2::new(point.0, point.1)); - let scale = translate * Mat3::from_scale(Vec2::new(scale.0, scale.1)); - let matrix = scale * Mat3::from_translation(Vec2::new(-point.0, -point.1)); + let translate_to_origin = Mat3::from_translation(Vec2::new(-point.0, -point.1)); + let scale = Mat3::from_scale(Vec2::new(scale.0, scale.1)); + let translate_back = Mat3::from_translation(Vec2::new(point.0, point.1)); + + let matrix = translate_back * scale * translate_to_origin * old; *self.matrix() = Some(matrix); self } diff --git a/crates/notan_egui/Cargo.toml b/crates/notan_egui/Cargo.toml index 7d8a5026..d542832e 100644 --- a/crates/notan_egui/Cargo.toml +++ b/crates/notan_egui/Cargo.toml @@ -1,24 +1,29 @@ [package] name = "notan_egui" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides EGUI support for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -egui = { version = "0.20.1", features = ["bytemuck"] } -bytemuck = "1.13.0" -notan_core = { path = "../notan_core", version = "0.9.5" } -notan_app = { path = "../notan_app", version = "0.9.5" } -notan_macro = { path = "../notan_macro", version = "0.9.5" } +notan_core.workspace = true +notan_app.workspace = true +notan_macro.workspace = true + +log.workspace = true +bytemuck.workspace = true + +egui = { version = "0.33.0", features = ["bytemuck"] } [features] links = [] drop_files = [] +glsl-to-spirv = ["notan_macro/glsl-to-spirv"] +shaderc = ["notan_macro/shaderc"] +serde = ["egui/serde"] diff --git a/crates/notan_egui/README.md b/crates/notan_egui/README.md index 187cc7ea..e34739d3 100644 --- a/crates/notan_egui/README.md +++ b/crates/notan_egui/README.md @@ -1,9 +1,9 @@ EGUI === -This is the implementation of [egui 0.20](https://github.com/emilk/egui) for notan. +This is the implementation of [egui 0.27.2](https://github.com/emilk/egui) for notan. -It should support all the features that __egui__ uses. Only `clipboard` remains unsupported for now. +It should support all the features that __egui__ uses. You can check some examples at `/examples` or check the demos online: * [egui_basic](https://nazariglez.github.io/notan-web/examples/egui_basic.html) diff --git a/crates/notan_egui/src/extension.rs b/crates/notan_egui/src/extension.rs index bbd20b2c..678d74c6 100644 --- a/crates/notan_egui/src/extension.rs +++ b/crates/notan_egui/src/extension.rs @@ -3,6 +3,7 @@ use crate::epaint::Primitive; use crate::plugin::Output; use crate::TextureId; +use egui::load::SizedTexture; use egui::{PaintCallbackInfo, Rect}; use notan_app::{ BlendFactor, BlendMode, Buffer, CullMode, Device, Graphics, Pipeline, RenderTexture, @@ -25,12 +26,15 @@ const EGUI_VERTEX: ShaderSource = notan_macro::vertex_shader! { layout(location = 0) out vec4 v_rgba_in_gamma; layout(location = 1) out vec2 v_tc; + layout(location = 2) out float v_srgb_enabled; layout(set = 0, binding = 0) uniform Locals { vec2 u_screen_size; + float srgb_enabled; }; void main() { + v_srgb_enabled = srgb_enabled; gl_Position = vec4( 2.0 * a_pos.x / u_screen_size.x - 1.0, 1.0 - 2.0 * a_pos.y / u_screen_size.y, @@ -48,18 +52,16 @@ const EGUI_VERTEX: ShaderSource = notan_macro::vertex_shader! { const EGUI_FRAGMENT: ShaderSource = notan_macro::fragment_shader! { r#" #version 450 - - #ifdef GL_ES - precision mediump float; - #endif + precision mediump float; layout(location = 0) in vec4 v_rgba_in_gamma; layout(location = 1) in vec2 v_tc; - - layout(binding = 0) uniform sampler2D u_sampler; + layout(location = 2) in float v_srgb_enabled; layout(location = 0) out vec4 color; + layout(binding = 0) uniform sampler2D u_sampler; + // 0-1 sRGB gamma from 0-1 linear vec3 srgb_gamma_from_linear(vec3 rgb) { bvec3 cutoff = lessThan(rgb, vec3(0.0031308)); @@ -74,11 +76,10 @@ const EGUI_FRAGMENT: ShaderSource = notan_macro::fragment_shader! { } void main() { - #if SRGB_TEXTURES - vec4 texture_in_gamma = srgba_gamma_from_linear(texture(u_sampler, v_tc)); - #else vec4 texture_in_gamma = texture(u_sampler, v_tc); - #endif + if (v_srgb_enabled == 1.0) { + texture_in_gamma = srgba_gamma_from_linear(texture_in_gamma); + } // Multiply vertex color with texture color (in linear space). color = v_rgba_in_gamma * texture_in_gamma; } @@ -123,6 +124,7 @@ impl EguiExtension { BlendFactor::InverseDestinationAlpha, BlendFactor::One, )) + .with_srgb_space(cfg!(target_arch = "wasm32")) .with_cull_mode(CullMode::None) .with_texture_location(0, "u_sampler") .build()?; @@ -132,26 +134,31 @@ impl EguiExtension { let ebo = gfx.create_index_buffer().build()?; let ubo = gfx .create_uniform_buffer(0, "Locals") - .with_data(&[0.0; 2]) + .with_data(&[0.0; 3]) .build()?; + let mut textures = HashMap::new(); + let fonts_texture = create_empty_texture(gfx, 0, 0)?; + textures.insert(egui::TextureId::default(), fonts_texture); + Ok(Self { pipeline, vbo, ebo, ubo, - textures: HashMap::new(), + textures, }) } - pub fn add_texture(&mut self, texture: &Texture) -> egui::TextureId { + pub fn add_texture(&mut self, texture: &Texture) -> SizedTexture { let id = egui::TextureId::User(texture.id()); + let size: egui::Vec2 = texture.size().into(); self.textures.insert(id, texture.clone()); - id + SizedTexture { id, size } } - pub fn remove_texture(&mut self, id: egui::TextureId) { - self.free_texture(id); + pub fn remove_texture(&mut self, id: impl Into) { + self.free_texture(id.into()); } fn set_texture( @@ -166,82 +173,42 @@ impl EguiExtension { if let Some([x, y]) = delta.pos { let texture = self .textures - .get_mut(&id) - .ok_or_else(|| format!("Failed to find EGUI texture {id:?}"))?; - - match &delta.image { - egui::ImageData::Color(image) => { - debug_assert_eq!( - image.width() * image.height(), - image.pixels.len(), - "Mismatch between texture size and texel count" - ); - - let data = bytemuck::cast_slice(image.pixels.as_ref()); - update_texture( - device, - texture, - data, - x as _, - y as _, - width as _, - height as _, - )? - } - egui::ImageData::Font(image) => { - debug_assert_eq!( - image.width() * image.height(), - image.pixels.len(), - "Mismatch between texture size and texel count" - ); - - let data: Vec = image - .srgba_pixels(None) - .flat_map(|a| a.to_array()) - .collect(); - - update_texture( - device, - texture, - &data, - x as _, - y as _, - width as _, - height as _, - )? - } - } + .entry(id) + .or_insert_with(|| create_empty_texture(device, width as _, height as _).unwrap()); + + let egui::ImageData::Color(image) = &delta.image; + + debug_assert_eq!( + image.width() * image.height(), + image.pixels.len(), + "Mismatch between texture size and texel count" + ); + + let data = bytemuck::cast_slice(image.pixels.as_ref()); + update_texture( + device, + texture, + data, + x as _, + y as _, + width as _, + height as _, + )?; return Ok(()); } // create a new texture - let texture = match &delta.image { - egui::ImageData::Color(image) => { - debug_assert_eq!( - image.width() * image.height(), - image.pixels.len(), - "Mismatch between texture size and texel count" - ); - - let data = bytemuck::cast_slice(image.pixels.as_ref()); - create_texture(device, data, width as _, height as _)? - } - egui::ImageData::Font(image) => { - debug_assert_eq!( - image.width() * image.height(), - image.pixels.len(), - "Mismatch between texture size and texel count" - ); - - let data: Vec = image - .srgba_pixels(None) - .flat_map(|a| a.to_array()) - .collect(); - - create_texture(device, &data, width as _, height as _)? - } - }; + let egui::ImageData::Color(image) = &delta.image; + + debug_assert_eq!( + image.width() * image.height(), + image.pixels.len(), + "Mismatch between texture size and texel count" + ); + + let data = bytemuck::cast_slice(image.pixels.as_ref()); + let texture = create_texture(device, data, width as _, height as _)?; self.textures.insert(id, texture); Ok(()) @@ -257,12 +224,13 @@ impl EguiExtension { meshes: Vec, textures_delta: &egui::TexturesDelta, target: Option<&RenderTexture>, + zoom_factor: f32, ) -> Result<(), String> { for (id, image_delta) in &textures_delta.set { self.set_texture(device, *id, image_delta)?; } - self.paint_primitives(device, meshes, target)?; + self.paint_primitives(device, meshes, target, zoom_factor)?; for &id in &textures_delta.free { self.free_texture(id); @@ -276,14 +244,12 @@ impl EguiExtension { device: &mut Device, meshes: Vec, target: Option<&RenderTexture>, + zoom_factor: f32, ) -> Result<(), String> { let (width, height) = target.map_or(device.size(), |rt| { (rt.base_width() as _, rt.base_height() as _) }); - let uniforms: [f32; 3] = [width as _, height as _, 0.0]; - device.set_buffer_data(&self.ubo, &uniforms); - for egui::ClippedPrimitive { clip_rect, primitive, @@ -291,7 +257,13 @@ impl EguiExtension { { match primitive { Primitive::Mesh(mesh) => { - self.paint_mesh(device, *clip_rect, mesh, target)?; + self.paint_mesh( + device, + zoom_factor * (*clip_rect), + mesh, + target, + zoom_factor, + )?; } Primitive::Callback(callback) => { let rect = Rect { @@ -301,8 +273,8 @@ impl EguiExtension { if callback.rect.is_positive() { let info = egui::PaintCallbackInfo { - viewport: callback.rect, - clip_rect: rect, + viewport: zoom_factor * callback.rect, + clip_rect: zoom_factor * rect, pixels_per_point: device.dpi() as _, screen_size_px: [width as _, height as _], }; @@ -327,15 +299,31 @@ impl EguiExtension { clip_rect: egui::Rect, primitive: &egui::Mesh, target: Option<&RenderTexture>, + zoom_factor: f32, ) -> Result<(), String> { - let vertices: &[f32] = bytemuck::cast_slice(&primitive.vertices); - device.set_buffer_data(&self.vbo, vertices); - device.set_buffer_data(&self.ebo, &primitive.indices); - let (width_in_pixels, height_in_pixels) = target.map_or(device.size(), |rt| { (rt.base_width() as _, rt.base_height() as _) }); + let texture = self + .textures + .get(&primitive.texture_id) + .ok_or_else(|| format!("Invalid EGUI texture id {:?}", &primitive.texture_id))?; + + let is_srgb_texture = matches!(texture.format(), TextureFormat::SRgba8); + let srgb_enabled = cfg!(target_arch = "wasm32") && is_srgb_texture; + let srgb_as_float = if srgb_enabled { 1.0 } else { 0.0 }; + let uniforms: [f32; 3] = [ + (width_in_pixels as f32) / zoom_factor, + (height_in_pixels as f32) / zoom_factor, + srgb_as_float, + ]; + device.set_buffer_data(&self.ubo, &uniforms); + + let vertices: &[f32] = bytemuck::cast_slice(&primitive.vertices); + device.set_buffer_data(&self.vbo, vertices); + device.set_buffer_data(&self.ebo, &primitive.indices); + let clip_min_x = clip_rect.min.x; let clip_min_y = clip_rect.min.y; let clip_max_x = clip_rect.max.x; @@ -355,11 +343,6 @@ impl EguiExtension { let width = clip_max_x - clip_min_x; let height = clip_max_y - clip_min_y; - let texture = self - .textures - .get(&primitive.texture_id) - .ok_or_else(|| format!("Invalid EGUI texture id {:?}", &primitive.texture_id))?; - // render pass let mut renderer = device.create_renderer(); renderer.set_scissors(clip_min_x, clip_min_y, width, height); @@ -380,18 +363,18 @@ impl EguiExtension { } pub trait EguiRegisterTexture { - fn egui_register_texture(&mut self, texture: &Texture) -> egui::TextureId; - fn egui_remove_texture(&mut self, id: egui::TextureId); + fn egui_register_texture(&mut self, texture: &Texture) -> egui::load::SizedTexture; + fn egui_remove_texture(&mut self, id: impl Into); } impl EguiRegisterTexture for Graphics { - fn egui_register_texture(&mut self, texture: &Texture) -> TextureId { + fn egui_register_texture(&mut self, texture: &Texture) -> SizedTexture { self.extension_mut::() .unwrap() .add_texture(texture) } - fn egui_remove_texture(&mut self, id: TextureId) { + fn egui_remove_texture(&mut self, id: impl Into) { self.extension_mut::() .unwrap() .remove_texture(id); @@ -402,14 +385,48 @@ impl EguiRegisterTexture for Graphics { fn create_texture( device: &mut Device, data: &[u8], - width: i32, - height: i32, + width: u32, + height: u32, ) -> Result { + let texture_format = if cfg!(target_arch = "wasm32") { + TextureFormat::SRgba8 + } else { + TextureFormat::Rgba32 + }; + + let texture_filter = if cfg!(target_arch = "wasm32") { + TextureFilter::Linear + } else { + TextureFilter::Nearest + }; + device .create_texture() .from_bytes(data, width, height) - .with_format(TextureFormat::Rgba32) - .with_filter(TextureFilter::Linear, TextureFilter::Linear) + .with_format(texture_format) + .with_filter(texture_filter, texture_filter) + .build() +} + +#[inline] +fn create_empty_texture(device: &mut Device, width: u32, height: u32) -> Result { + let texture_format = if cfg!(target_arch = "wasm32") { + TextureFormat::SRgba8 + } else { + TextureFormat::Rgba32 + }; + + let texture_filter = if cfg!(target_arch = "wasm32") { + TextureFilter::Linear + } else { + TextureFilter::Nearest + }; + + device + .create_texture() + .from_empty_buffer(width, height) + .with_format(texture_format) + .with_filter(texture_filter, texture_filter) .build() } @@ -418,10 +435,10 @@ fn update_texture( device: &mut Device, texture: &mut Texture, data: &[u8], - x: i32, - y: i32, - width: i32, - height: i32, + x: u32, + y: u32, + width: u32, + height: u32, ) -> Result<(), String> { device .update_texture(texture) diff --git a/crates/notan_egui/src/input.rs b/crates/notan_egui/src/input.rs index a5481846..847abfa9 100644 --- a/crates/notan_egui/src/input.rs +++ b/crates/notan_egui/src/input.rs @@ -1,4 +1,4 @@ -use egui::PointerButton; +use egui::{Key, PointerButton}; use notan_core::keyboard::KeyCode; use notan_core::mouse::MouseButton; @@ -7,89 +7,122 @@ pub(crate) fn to_egui_pointer(btn: &MouseButton) -> Option MouseButton::Left => PointerButton::Primary, MouseButton::Right => PointerButton::Secondary, MouseButton::Middle => PointerButton::Middle, + MouseButton::Back => PointerButton::Extra1, + MouseButton::Forward => PointerButton::Extra2, MouseButton::Other(_) => return None, }) } -pub(crate) fn to_egui_key(key: &KeyCode) -> Option { +pub(crate) fn to_egui_key(key: &KeyCode) -> Option { Some(match key { - KeyCode::Down => egui::Key::ArrowDown, - KeyCode::Left => egui::Key::ArrowLeft, - KeyCode::Right => egui::Key::ArrowRight, - KeyCode::Up => egui::Key::ArrowUp, + KeyCode::ArrowDown => Key::ArrowDown, + KeyCode::ArrowLeft => Key::ArrowLeft, + KeyCode::ArrowRight => Key::ArrowRight, + KeyCode::ArrowUp => Key::ArrowUp, - KeyCode::Escape => egui::Key::Escape, - KeyCode::Tab => egui::Key::Tab, - KeyCode::Back => egui::Key::Backspace, - KeyCode::Return => egui::Key::Enter, - KeyCode::Space => egui::Key::Space, + KeyCode::Escape => Key::Escape, + KeyCode::Tab => Key::Tab, + KeyCode::Backspace => Key::Backspace, + KeyCode::Enter | KeyCode::NumpadEnter => Key::Enter, - KeyCode::Insert => egui::Key::Insert, - KeyCode::Delete => egui::Key::Delete, - KeyCode::Home => egui::Key::Home, - KeyCode::End => egui::Key::End, - KeyCode::PageUp => egui::Key::PageUp, - KeyCode::PageDown => egui::Key::PageDown, + KeyCode::Insert => Key::Insert, + KeyCode::Delete => Key::Delete, + KeyCode::Home => Key::Home, + KeyCode::End => Key::End, + KeyCode::PageUp => Key::PageUp, + KeyCode::PageDown => Key::PageDown, - KeyCode::Key0 => egui::Key::Num0, - KeyCode::Key1 => egui::Key::Num1, - KeyCode::Key2 => egui::Key::Num2, - KeyCode::Key3 => egui::Key::Num3, - KeyCode::Key4 => egui::Key::Num4, - KeyCode::Key5 => egui::Key::Num5, - KeyCode::Key6 => egui::Key::Num6, - KeyCode::Key7 => egui::Key::Num7, - KeyCode::Key8 => egui::Key::Num8, - KeyCode::Key9 => egui::Key::Num9, + KeyCode::Space => Key::Space, + KeyCode::Comma => Key::Comma, + KeyCode::Period => Key::Period, + KeyCode::Semicolon => Key::Semicolon, + KeyCode::Backslash => Key::Backslash, + KeyCode::Slash | KeyCode::NumpadDivide => Key::Slash, + KeyCode::BracketLeft => Key::OpenBracket, + KeyCode::BracketRight => Key::CloseBracket, + KeyCode::Backquote => Key::Backtick, + KeyCode::Quote => Key::Quote, - KeyCode::A => egui::Key::A, - KeyCode::B => egui::Key::B, - KeyCode::C => egui::Key::C, - KeyCode::D => egui::Key::D, - KeyCode::E => egui::Key::E, - KeyCode::F => egui::Key::F, - KeyCode::G => egui::Key::G, - KeyCode::H => egui::Key::H, - KeyCode::I => egui::Key::I, - KeyCode::J => egui::Key::J, - KeyCode::K => egui::Key::K, - KeyCode::L => egui::Key::L, - KeyCode::M => egui::Key::M, - KeyCode::N => egui::Key::N, - KeyCode::O => egui::Key::O, - KeyCode::P => egui::Key::P, - KeyCode::Q => egui::Key::Q, - KeyCode::R => egui::Key::R, - KeyCode::S => egui::Key::S, - KeyCode::T => egui::Key::T, - KeyCode::U => egui::Key::U, - KeyCode::V => egui::Key::V, - KeyCode::W => egui::Key::W, - KeyCode::X => egui::Key::X, - KeyCode::Y => egui::Key::Y, - KeyCode::Z => egui::Key::Z, + KeyCode::Cut => Key::Cut, + KeyCode::Copy => Key::Copy, + KeyCode::Paste => Key::Paste, + KeyCode::Minus | KeyCode::NumpadSubtract => Key::Minus, + KeyCode::NumpadAdd => Key::Plus, + KeyCode::Equal => Key::Equals, - KeyCode::F1 => egui::Key::F1, - KeyCode::F2 => egui::Key::F2, - KeyCode::F3 => egui::Key::F3, - KeyCode::F4 => egui::Key::F4, - KeyCode::F5 => egui::Key::F5, - KeyCode::F6 => egui::Key::F6, - KeyCode::F7 => egui::Key::F7, - KeyCode::F8 => egui::Key::F8, - KeyCode::F9 => egui::Key::F9, - KeyCode::F10 => egui::Key::F10, - KeyCode::F11 => egui::Key::F11, - KeyCode::F12 => egui::Key::F12, - KeyCode::F13 => egui::Key::F13, - KeyCode::F14 => egui::Key::F14, - KeyCode::F15 => egui::Key::F15, - KeyCode::F16 => egui::Key::F16, - KeyCode::F17 => egui::Key::F17, - KeyCode::F18 => egui::Key::F18, - KeyCode::F19 => egui::Key::F19, - KeyCode::F20 => egui::Key::F20, + KeyCode::Digit0 | KeyCode::Numpad0 => Key::Num0, + KeyCode::Digit1 | KeyCode::Numpad1 => Key::Num1, + KeyCode::Digit2 | KeyCode::Numpad2 => Key::Num2, + KeyCode::Digit3 | KeyCode::Numpad3 => Key::Num3, + KeyCode::Digit4 | KeyCode::Numpad4 => Key::Num4, + KeyCode::Digit5 | KeyCode::Numpad5 => Key::Num5, + KeyCode::Digit6 | KeyCode::Numpad6 => Key::Num6, + KeyCode::Digit7 | KeyCode::Numpad7 => Key::Num7, + KeyCode::Digit8 | KeyCode::Numpad8 => Key::Num8, + KeyCode::Digit9 | KeyCode::Numpad9 => Key::Num9, + KeyCode::KeyA => Key::A, + KeyCode::KeyB => Key::B, + KeyCode::KeyC => Key::C, + KeyCode::KeyD => Key::D, + KeyCode::KeyE => Key::E, + KeyCode::KeyF => Key::F, + KeyCode::KeyG => Key::G, + KeyCode::KeyH => Key::H, + KeyCode::KeyI => Key::I, + KeyCode::KeyJ => Key::J, + KeyCode::KeyK => Key::K, + KeyCode::KeyL => Key::L, + KeyCode::KeyM => Key::M, + KeyCode::KeyN => Key::N, + KeyCode::KeyO => Key::O, + KeyCode::KeyP => Key::P, + KeyCode::KeyQ => Key::Q, + KeyCode::KeyR => Key::R, + KeyCode::KeyS => Key::S, + KeyCode::KeyT => Key::T, + KeyCode::KeyU => Key::U, + KeyCode::KeyV => Key::V, + KeyCode::KeyW => Key::W, + KeyCode::KeyX => Key::X, + KeyCode::KeyY => Key::Y, + KeyCode::KeyZ => Key::Z, + + KeyCode::F1 => Key::F1, + KeyCode::F2 => Key::F2, + KeyCode::F3 => Key::F3, + KeyCode::F4 => Key::F4, + KeyCode::F5 => Key::F5, + KeyCode::F6 => Key::F6, + KeyCode::F7 => Key::F7, + KeyCode::F8 => Key::F8, + KeyCode::F9 => Key::F9, + KeyCode::F10 => Key::F10, + KeyCode::F11 => Key::F11, + KeyCode::F12 => Key::F12, + KeyCode::F13 => Key::F13, + KeyCode::F14 => Key::F14, + KeyCode::F15 => Key::F15, + KeyCode::F16 => Key::F16, + KeyCode::F17 => Key::F17, + KeyCode::F18 => Key::F18, + KeyCode::F19 => Key::F19, + KeyCode::F20 => Key::F20, + KeyCode::F21 => Key::F21, + KeyCode::F22 => Key::F22, + KeyCode::F23 => Key::F23, + KeyCode::F24 => Key::F24, + KeyCode::F25 => Key::F25, + KeyCode::F26 => Key::F26, + KeyCode::F27 => Key::F27, + KeyCode::F28 => Key::F28, + KeyCode::F29 => Key::F29, + KeyCode::F30 => Key::F30, + KeyCode::F31 => Key::F31, + KeyCode::F32 => Key::F32, + KeyCode::F33 => Key::F33, + KeyCode::F34 => Key::F34, + KeyCode::F35 => Key::F35, _ => return None, }) } diff --git a/crates/notan_egui/src/lib.rs b/crates/notan_egui/src/lib.rs index 92ad0c1c..cba26831 100644 --- a/crates/notan_egui/src/lib.rs +++ b/crates/notan_egui/src/lib.rs @@ -7,4 +7,5 @@ pub use config::EguiConfig; pub use extension::{EguiCallbackFn, EguiExtension, EguiRegisterTexture}; pub use plugin::{EguiPlugin, EguiPluginSugar}; +pub use egui::load::SizedTexture; pub use egui::*; diff --git a/crates/notan_egui/src/plugin.rs b/crates/notan_egui/src/plugin.rs index e26d5af9..98bb61b4 100644 --- a/crates/notan_egui/src/plugin.rs +++ b/crates/notan_egui/src/plugin.rs @@ -18,17 +18,23 @@ pub struct EguiPlugin { platform_output: Option, latest_evt_was_touch: bool, needs_repaint: bool, + pixels_per_point: f32, } #[allow(clippy::derivable_impls)] impl Default for EguiPlugin { fn default() -> Self { + let ctx: egui::Context = Default::default(); + if cfg!(target_arch = "wasm32") { + ctx.options_mut(|opt| opt.zoom_with_keyboard = false); + } Self { - ctx: Default::default(), + ctx, raw_input: Default::default(), platform_output: Default::default(), latest_evt_was_touch: Default::default(), needs_repaint: Default::default(), + pixels_per_point: Default::default(), } } } @@ -39,17 +45,22 @@ impl EguiPlugin { self.raw_input.events.push(evt); } - pub fn run(&mut self, run_ui: impl FnOnce(&egui::Context)) -> Output { + pub fn run(&mut self, run_ui: impl FnMut(&egui::Context)) -> Output { let new_input = self.raw_input.take(); let egui::FullOutput { platform_output, - repaint_after, textures_delta, shapes, + pixels_per_point, + viewport_output, } = self.ctx.run(new_input, run_ui); - let needs_repaint = repaint_after.is_zero(); + let needs_update_textures = !textures_delta.is_empty(); + let needs_repaint = viewport_output + .values() + .any(|output| output.repaint_delay.is_zero()) + || needs_update_textures; // On post frame needs repaint is set to false // set it again if true after a egui output. @@ -61,30 +72,24 @@ impl EguiPlugin { Output { ctx: self.ctx.clone(), - shapes: RefCell::new(Some(shapes)), + shapes: RefCell::new(Some((shapes, pixels_per_point))), textures_delta, clear_color: None, - needs_repaint, } } } pub struct Output { ctx: egui::Context, - shapes: RefCell>>, + shapes: RefCell, f32)>>, textures_delta: egui::TexturesDelta, clear_color: Option, - needs_repaint: bool, } impl Output { pub fn clear_color(&mut self, color: Color) { self.clear_color = Some(color); } - - pub fn needs_repaint(&self) -> bool { - self.needs_repaint - } } impl GfxExtension for EguiExtension {} @@ -100,10 +105,10 @@ impl GfxRenderer for Output { "Missing EguiExtension. You may need to add 'EguiConfig' to notan.".to_string() })?; - if let Some(shapes) = self.shapes.borrow_mut().take() { + if let Some((shapes, pixels_per_point)) = self.shapes.borrow_mut().take() { if self.clear_color.is_some() { let mut clear_renderer = device.create_renderer(); - clear_renderer.begin(Some(&ClearOptions { + clear_renderer.begin(Some(ClearOptions { color: self.clear_color, ..Default::default() })); @@ -115,14 +120,30 @@ impl GfxRenderer for Output { } } - let meshes = self.ctx.tessellate(shapes); - ext.paint_and_update_textures(device, meshes, &self.textures_delta, target)?; + let meshes = self.ctx.tessellate(shapes, pixels_per_point); + ext.paint_and_update_textures( + device, + meshes, + &self.textures_delta, + target, + self.ctx.zoom_factor(), + )?; } Ok(()) } } +impl EguiPlugin { + fn egui_mouse_pos(&self, app: &App) -> egui::Pos2 { + self.egui_pos(app.mouse.x, app.mouse.y) + } + + fn egui_pos(&self, x: f32, y: f32) -> egui::Pos2 { + egui::Pos2::new(x, y) / self.ctx.zoom_factor() + } +} + impl Plugin for EguiPlugin { fn event( &mut self, @@ -157,14 +178,13 @@ impl Plugin for EguiPlugin { Event::ScreenAspectChange { .. } => { self.ctx.request_repaint(); } - Event::MouseMove { .. } => self.add_event(egui::Event::PointerMoved(egui::Pos2::new( - app.mouse.x, - app.mouse.y, - ))), + Event::MouseMove { .. } => { + self.add_event(egui::Event::PointerMoved(self.egui_mouse_pos(app))) + } Event::MouseDown { button, .. } => { if let Some(btn) = to_egui_pointer(button) { self.add_event(egui::Event::PointerButton { - pos: egui::Pos2::new(app.mouse.x, app.mouse.y), + pos: self.egui_mouse_pos(app), button: btn, pressed: true, modifiers, @@ -174,7 +194,7 @@ impl Plugin for EguiPlugin { Event::MouseUp { button, .. } => { if let Some(btn) = to_egui_pointer(button) { self.add_event(egui::Event::PointerButton { - pos: egui::Pos2::new(app.mouse.x, app.mouse.y), + pos: self.egui_mouse_pos(app), button: btn, pressed: false, modifiers, @@ -189,10 +209,12 @@ impl Plugin for EguiPlugin { if modifiers.ctrl || modifiers.command { let factor = (delta_y / 200.0).exp(); self.add_event(egui::Event::Zoom(factor)); - } else if cfg!(target_os = "macos") && modifiers.shift { - self.add_event(egui::Event::Scroll(egui::vec2(delta_x + delta_y, 0.0))); } else { - self.add_event(egui::Event::Scroll(egui::vec2(*delta_x, *delta_y))); + self.add_event(egui::Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: self.egui_pos(*delta_x, *delta_y).to_vec2(), + modifiers, + }); } } Event::MouseEnter { .. } => {} @@ -201,7 +223,9 @@ impl Plugin for EguiPlugin { if let Some(key) = to_egui_key(key) { self.add_event(egui::Event::Key { key, + physical_key: None, pressed: true, + repeat: false, modifiers, }) } @@ -211,7 +235,9 @@ impl Plugin for EguiPlugin { if let Some(key) = to_egui_key(key) { self.add_event(egui::Event::Key { key, + physical_key: None, pressed: false, + repeat: false, modifiers, }) } @@ -251,23 +277,23 @@ impl Plugin for EguiPlugin { device_id: egui::TouchDeviceId(0), id: egui::TouchId(*id), phase: egui::TouchPhase::Start, - pos: (*x, *y).into(), - force: 0.0, + pos: self.egui_pos(*x, *y), + force: Some(0.0), }), Event::TouchMove { id, x, y } => self.add_event(egui::Event::Touch { device_id: egui::TouchDeviceId(0), id: egui::TouchId(*id), phase: egui::TouchPhase::Move, - pos: (*x, *y).into(), - force: 0.0, + pos: self.egui_pos(*x, *y), + force: Some(0.0), }), Event::TouchEnd { id, x, y } => { self.add_event(egui::Event::Touch { device_id: egui::TouchDeviceId(0), id: egui::TouchId(*id), phase: egui::TouchPhase::End, - pos: (*x, *y).into(), - force: 0.0, + pos: self.egui_pos(*x, *y), + force: Some(0.0), }); is_touch_end = true; @@ -277,11 +303,12 @@ impl Plugin for EguiPlugin { device_id: egui::TouchDeviceId(0), id: egui::TouchId(*id), phase: egui::TouchPhase::Cancel, - pos: (*x, *y).into(), - force: 0.0, + pos: self.egui_pos(*x, *y), + force: Some(0.0), }); is_touch_end = true; } + _ => {} } self.latest_evt_was_touch = is_touch_end; @@ -290,14 +317,23 @@ impl Plugin for EguiPlugin { } fn update(&mut self, app: &mut App, _assets: &mut Assets) -> Result { - self.raw_input.pixels_per_point = Some(app.window().dpi() as _); - self.raw_input.time = Some(app.timer.time_since_init() as _); + let dpi = app.window().dpi() as f32; + self.pixels_per_point = dpi * self.ctx.zoom_factor(); + if let Some(viewport) = self + .raw_input + .viewports + .get_mut(&self.raw_input.viewport_id) + { + viewport.native_pixels_per_point = Some(dpi); + } + + self.raw_input.time = Some(app.timer.elapsed_f32() as _); self.raw_input.predicted_dt = app.timer.delta_f32(); let (w, h) = app.window().size(); self.raw_input.screen_rect = Some(egui::Rect { min: egui::pos2(0.0, 0.0), - max: egui::pos2(w as _, h as _), + max: egui::pos2(w as _, h as _) / self.ctx.zoom_factor(), }); Ok(AppFlow::Next) } @@ -311,9 +347,7 @@ impl Plugin for EguiPlugin { if let Some(platform_output) = self.platform_output.take() { let egui::PlatformOutput { cursor_icon, - open_url, - - copied_text, + commands, .. } = platform_output; @@ -325,21 +359,24 @@ impl Plugin for EguiPlugin { } } - #[cfg(not(feature = "links"))] - let _ = open_url; + commands.iter().for_each(|cmd| match cmd { + egui::OutputCommand::CopyText(copied_text) => { + if !copied_text.is_empty() { + app.backend.set_clipboard_text(copied_text); + } + } - #[cfg(feature = "links")] - if let Some(OpenUrl { url, new_tab }) = open_url { - if new_tab { - app.open_link_new_tab(&url); - } else { - app.open_link(&url); + #[cfg(feature = "links")] + egui::OutputCommand::OpenUrl(OpenUrl { url, new_tab }) => { + if *new_tab { + app.open_link_new_tab(url); + } else { + app.open_link(url); + } } - } - if !copied_text.is_empty() { - app.backend.set_clipboard_text(&copied_text); - } + _ => {} + }); } self.needs_repaint = false; @@ -402,11 +439,11 @@ fn is_printable(chr: char, modifiers: &egui::Modifiers) -> bool { } pub trait EguiPluginSugar { - fn egui(&mut self, run_ui: impl FnOnce(&egui::Context)) -> Output; + fn egui(&mut self, run_ui: impl FnMut(&egui::Context)) -> Output; } impl EguiPluginSugar for Plugins { - fn egui(&mut self, run_ui: impl FnOnce(&Context)) -> Output { + fn egui(&mut self, run_ui: impl FnMut(&Context)) -> Output { let mut ext = self.get_mut::().unwrap(); ext.run(run_ui) } diff --git a/crates/notan_extra/Cargo.toml b/crates/notan_extra/Cargo.toml index 00ae0af5..f6977c22 100644 --- a/crates/notan_extra/Cargo.toml +++ b/crates/notan_extra/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "notan_extra" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides extra features or plugins for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -notan_app = { path = "../notan_app", version = "0.9.5" } +notan_app.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -spin_sleep = "1.1.1" +spin_sleep = "1.3.0" diff --git a/crates/notan_glow/Cargo.toml b/crates/notan_glow/Cargo.toml index c4d330f1..6f99e691 100644 --- a/crates/notan_glow/Cargo.toml +++ b/crates/notan_glow/Cargo.toml @@ -1,25 +1,27 @@ [package] name = "notan_glow" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides support for OpenGL, OpenGL ES and WebGL for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -bytemuck = "1.13.0" -glow = "0.12.1" -notan_graphics = { path= "../notan_graphics", version = "0.9.5" } -hashbrown = "0.13.2" -image = { version = "0.24.5", default-features = false, features = ["jpeg", "png"] } +notan_graphics.workspace = true + +log.workspace = true +bytemuck.workspace = true +hashbrown.workspace = true +image.workspace = true + +glow = "0.16.0" [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = "0.2.83" -js-sys = "0.3.60" -web-sys = { version = "0.3.60", features = ["Window", "WebGlContextAttributes","HtmlCanvasElement","HtmlImageElement","VideoFrame"] } +wasm-bindgen.workspace = true +js-sys.workspace = true +web-sys = { workspace = true, features = ["Window", "WebGlContextAttributes", "WebGlPowerPreference", "HtmlCanvasElement", "HtmlImageElement", "VideoFrame"] } diff --git a/crates/notan_glow/src/buffer.rs b/crates/notan_glow/src/buffer.rs index 542a76e3..03c7bad1 100644 --- a/crates/notan_glow/src/buffer.rs +++ b/crates/notan_glow/src/buffer.rs @@ -29,13 +29,12 @@ pub(crate) struct InnerBuffer { #[cfg(target_arch = "wasm32")] global_ubo: Option>, //Hack, wasm doesn't use the offset for std140 - pub block_binded: bool, - gpu_buff_size: usize, draw_usage: u32, draw_target: u32, pub(crate) kind: Kind, last_pipeline: Option, + block_dirty: bool, #[cfg(debug_assertions)] pub(crate) initialized: bool, @@ -48,7 +47,8 @@ impl InnerBuffer { #[cfg(target_arch = "wasm32")] let global_ubo = if matches!(kind, Kind::Uniform(_, _)) { - let max = unsafe { gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE) } as usize; + let max = (unsafe { gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE) } as usize) + .min(1 << 16); Some(vec![0; max]) } else { @@ -73,13 +73,12 @@ impl InnerBuffer { #[cfg(target_arch = "wasm32")] global_ubo, - block_binded: false, - gpu_buff_size: 0, draw_usage, draw_target, kind, last_pipeline: None, + block_dirty: true, #[cfg(debug_assertions)] initialized: false, @@ -88,10 +87,10 @@ impl InnerBuffer { #[inline] pub fn bind(&mut self, gl: &Context, pipeline_id: Option, reset_attrs: bool) { - let pipeline_changed = - pipeline_id.is_some() && (pipeline_id != self.last_pipeline || reset_attrs); - if pipeline_changed { + let pip_changed = pipeline_changed(pipeline_id, self.last_pipeline) || reset_attrs; + if pip_changed { self.last_pipeline = pipeline_id; + self.block_dirty = true; }; unsafe { @@ -99,7 +98,7 @@ impl InnerBuffer { match &self.kind { Kind::Vertex(attrs) => { - if pipeline_changed { + if pip_changed { attrs.enable(gl); } } @@ -143,9 +142,14 @@ impl InnerBuffer { } } - pub fn bind_ubo_block(&mut self, gl: &Context, pipeline: &InnerPipeline) { - self.block_binded = true; + pub fn bind_ubo_block(&mut self, gl: &Context, pipeline_id: u64, pipeline: &InnerPipeline) { + let pip_changed = + pipeline_changed(Some(pipeline_id), self.last_pipeline) || self.block_dirty; + if !pip_changed { + return; + } + self.block_dirty = false; if let Kind::Uniform(slot, name) = &self.kind { unsafe { if let Some(index) = gl.get_uniform_block_index(pipeline.program, name) { @@ -162,3 +166,7 @@ impl InnerBuffer { } } } + +fn pipeline_changed(pipeline_id: Option, last_pipeline: Option) -> bool { + pipeline_id.is_some() && pipeline_id != last_pipeline +} diff --git a/crates/notan_glow/src/html_image.rs b/crates/notan_glow/src/html_image.rs index 4ccae2d6..5a2be4fc 100644 --- a/crates/notan_glow/src/html_image.rs +++ b/crates/notan_glow/src/html_image.rs @@ -1,5 +1,3 @@ -#![cfg(target_arch = "wasm32")] - use crate::texture::{ post_create_texture, pre_create_texture, texture_format, texture_internal_format, texture_type, TexInfo, TextureKey, @@ -78,8 +76,8 @@ pub(crate) unsafe fn update_texture_from_html_image( gl.tex_sub_image_2d_with_html_image( glow::TEXTURE_2D, 0, - opts.x_offset, - opts.y_offset, + opts.x_offset as _, + opts.y_offset as _, texture_format(&opts.format), // 3d texture needs another value? texture_type(&opts.format), image, diff --git a/crates/notan_glow/src/lib.rs b/crates/notan_glow/src/lib.rs index 2b491859..4b798a54 100644 --- a/crates/notan_glow/src/lib.rs +++ b/crates/notan_glow/src/lib.rs @@ -26,7 +26,6 @@ use crate::texture::{texture_format, texture_type, TextureKey}; use crate::texture_source::{add_empty_texture, add_texture_from_bytes, add_texture_from_image}; use crate::to_glow::ToGlow; use buffer::InnerBuffer; -use notan_graphics::ResourceId::Texture; use pipeline::{InnerPipeline, VertexAttributes}; use render_target::InnerRenderTexture; use texture::InnerTexture; @@ -37,7 +36,7 @@ pub struct GlowBackend { texture_count: u64, pipeline_count: u64, render_target_count: u64, - size: (i32, i32), + size: (u32, u32), dpi: f32, pipelines: HashMap, buffers: HashMap, @@ -49,8 +48,7 @@ pub struct GlowBackend { limits: Limits, stats: GpuStats, current_uniforms: Vec, - drawing_srgba: bool, - drawing_to_render_texture: bool, + target_render_texture: Option, render_texture_mipmaps: bool, default_gl_framebuffer: Option, } @@ -67,13 +65,13 @@ impl GlowBackend { } #[cfg(all( - not(target_arch = "wasm32"), - not(target_os = "ios"), - not(target_os = "android") + not(target_arch = "wasm32"), + not(target_os = "ios"), + not(target_os = "android") ))] pub fn new(loader_function: F) -> Result - where - F: FnMut(&str) -> *const std::os::raw::c_void, + where + F: FnMut(&str) -> *const std::os::raw::c_void, { let gl = unsafe { Context::from_loader_function(loader_function) }; @@ -82,33 +80,36 @@ impl GlowBackend { #[cfg(any(target_os = "ios", target_os = "android"))] pub fn new(mut loader_function: F) -> Result - where - F: FnMut(&str) -> *const std::os::raw::c_void, + where + F: FnMut(&str) -> *const std::os::raw::c_void, { let gl = unsafe { Context::from_loader_function(loader_function) }; Self::from(gl, "opengl_es") } - fn get_default_frame_buffer(gl: &Context) -> Option { - let mut default_gl_framebuffer: Option = None; + fn get_default_frame_buffer(_gl: &Context) -> Option { #[cfg(target_os = "ios")] { - let default_gl_framebuffer_binding = unsafe { - gl.get_parameter_i32(glow::FRAMEBUFFER_BINDING) as u32 - }; - if default_gl_framebuffer_binding == 0 { - return None; + let default_gl_framebuffer_binding = + unsafe { _gl.get_parameter_i32(glow::FRAMEBUFFER_BINDING) as u32 }; + if default_gl_framebuffer_binding != 0 { + let non_zero_u32 = NonZeroU32::new(default_gl_framebuffer_binding).unwrap(); + return Some(NativeFramebuffer(non_zero_u32)); } - let non_zero_u32 = NonZeroU32::new(default_gl_framebuffer_binding).unwrap(); - let framebuffer = NativeFramebuffer(non_zero_u32); - default_gl_framebuffer = Some(framebuffer); } - return default_gl_framebuffer; + None } fn from(gl: Context, api: &str) -> Result { - log::info!("Using {} graphics api", api); + unsafe { + let version = gl.get_parameter_string(glow::VERSION); + let renderer = gl.get_parameter_string(glow::RENDERER); + let vendor = gl.get_parameter_string(glow::VENDOR); + log::info!( + "OpenGL Info: \nVersion: {version}\nRenderer: {renderer}\nVendor: {vendor}\n---" + ); + } let limits = unsafe { Limits { @@ -117,7 +118,7 @@ impl GlowBackend { } }; - let mut default_gl_framebuffer: Option = Self::get_default_frame_buffer(&gl); + let default_gl_framebuffer: Option = Self::get_default_frame_buffer(&gl); let stats = GpuStats::default(); Ok(Self { @@ -138,8 +139,7 @@ impl GlowBackend { limits, stats, current_uniforms: vec![], - drawing_srgba: false, - drawing_to_render_texture: false, + target_render_texture: None, render_texture_mipmaps: false, default_gl_framebuffer, }) @@ -153,34 +153,9 @@ impl GlowBackend { self.stats.misc += 1; } - #[inline] - fn enable_srgba(&mut self) { - if self.drawing_srgba { - return; - } - - self.drawing_srgba = true; - unsafe { - self.gl.enable(glow::FRAMEBUFFER_SRGB); - } - } - - #[inline] - fn disable_srgba(&mut self) { - if !self.drawing_srgba { - return; - } - - self.drawing_srgba = false; - unsafe { - self.gl.disable(glow::FRAMEBUFFER_SRGB); - } - } - - #[cfg(not(target_arch = "wasm32"))] pub fn get_gl_texture_id(&self, id: u64) -> Option { - self.textures.get(&id).map(|t| u32::from(t.texture.0.get())) + self.textures.get(&id).map(|t| t.texture.0.get()) } fn begin( @@ -198,15 +173,15 @@ impl GlowBackend { let (width, height, dpi) = match render_target { Some(rt) => { rt.bind(&self.gl); - self.drawing_to_render_texture = true; + self.target_render_texture = Some(rt.texture_id); self.render_texture_mipmaps = rt.use_mipmaps; (rt.size.0, rt.size.1, 1.0) } None => { unsafe { - self.gl.bind_framebuffer(glow::FRAMEBUFFER, self.default_gl_framebuffer); + self.gl + .bind_framebuffer(glow::FRAMEBUFFER, self.default_gl_framebuffer); } - self.drawing_to_render_texture = false; self.render_texture_mipmaps = false; (self.size.0, self.size.1, self.dpi) } @@ -219,7 +194,7 @@ impl GlowBackend { #[inline] fn viewport(&mut self, mut x: f32, mut y: f32, width: f32, height: f32, dpi: f32) { - if !self.drawing_to_render_texture { + if self.target_render_texture.is_none() { y = (self.size.1 as f32 - (height + y)) * dpi; x *= dpi; } @@ -235,7 +210,7 @@ impl GlowBackend { #[inline] fn scissors(&mut self, x: f32, y: f32, width: f32, height: f32, dpi: f32) { - let canvas_height = ((self.size.1 - (height + y) as i32) as f32 * dpi) as i32; + let canvas_height = ((self.size.1 - (height + y) as u32) as f32 * dpi) as _; let x = x * dpi; let width = width * dpi; let height = height * dpi; @@ -252,20 +227,28 @@ impl GlowBackend { fn end(&mut self) { unsafe { // generate mipmap for the framebuffer texture if needed - if self.drawing_to_render_texture && self.render_texture_mipmaps { - self.gl.generate_mipmap(glow::TEXTURE_2D); + if self.render_texture_mipmaps { + if let Some(render_texture) = self + .target_render_texture + .and_then(|id| self.textures.get(&id)) + { + self.gl + .bind_texture(glow::TEXTURE_2D, Some(render_texture.texture)); + self.gl.generate_mipmap(glow::TEXTURE_2D); + self.gl.bind_texture(glow::TEXTURE_2D, None); + } } - self.disable_srgba(); self.gl.disable(glow::SCISSOR_TEST); self.gl.bind_buffer(glow::ARRAY_BUFFER, None); self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); self.gl.bind_buffer(glow::UNIFORM_BUFFER, None); self.gl.bind_vertex_array(None); - self.gl.bind_framebuffer(glow::FRAMEBUFFER, self.default_gl_framebuffer); + self.gl + .bind_framebuffer(glow::FRAMEBUFFER, self.default_gl_framebuffer); } self.using_indices = None; - self.drawing_to_render_texture = false; + self.target_render_texture = None; self.render_texture_mipmaps = false; } @@ -280,7 +263,7 @@ impl GlowBackend { pip.bind(&self.gl, options); self.using_indices = None; self.current_pipeline = id; - self.current_uniforms = pip.uniform_locations.clone(); + self.current_uniforms.clone_from(&pip.uniform_locations); } } @@ -301,12 +284,11 @@ impl GlowBackend { false } Kind::Uniform(_slot, _name) => { - if !buffer.block_binded { - buffer.bind_ubo_block( - &self.gl, - self.pipelines.get(&self.current_pipeline).as_ref().unwrap(), - ); - } + buffer.bind_ubo_block( + &self.gl, + self.current_pipeline, + self.pipelines.get(&self.current_pipeline).as_ref().unwrap(), + ); false } Kind::Vertex(attrs) => match self.pipelines.get_mut(&self.current_pipeline) { @@ -321,10 +303,10 @@ impl GlowBackend { fn bind_texture(&mut self, id: u64, slot: u32, location: u32) { if let Some(pip) = self.pipelines.get(&self.current_pipeline) { - let is_srgba = if let Some(texture) = self.textures.get(&id) { + if let Some(texture) = self.textures.get(&id) { #[cfg(debug_assertions)] if !pip.texture_locations.contains_key(&location) { - log::warn!("Uniform location {} for texture {} should be declared when the pipeline is created.", location, id); + log::warn!("Uniform location {location} for texture {id} should be declared when the pipeline is created."); } let loc = pip @@ -332,16 +314,6 @@ impl GlowBackend { .get(&location) .unwrap_or_else(|| self.get_texture_uniform_loc(&location)); texture.bind(&self.gl, slot, loc); - texture.is_srgba - } else { - log::debug!("NOTAN_GLOW::NO_TEXTURE_FOUND"); - false - }; - - if is_srgba { - self.enable_srgba(); - } else { - self.disable_srgba(); } } } @@ -377,7 +349,11 @@ impl GlowBackend { } fn set_program_point_size(&self, primitive: &DrawPrimitive) { - #[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "android")))] + #[cfg(all( + not(target_arch = "wasm32"), + not(target_os = "ios"), + not(target_os = "android") + ))] unsafe { if matches!(primitive, DrawPrimitive::Points) { self.gl.enable(glow::PROGRAM_POINT_SIZE); @@ -519,7 +495,8 @@ impl DeviceBackend for GlowBackend { } fn render(&mut self, commands: &[Commands], target: Option) { - #[cfg(target_os = "ios")]{ + #[cfg(target_os = "ios")] + { let value = Self::get_default_frame_buffer(&self.gl); if value.is_some() { self.default_gl_framebuffer = value; @@ -564,13 +541,12 @@ impl DeviceBackend for GlowBackend { width, height, } => self.scissors(*x, *y, *width, *height, self.dpi), - } }); } fn clean(&mut self, to_clean: &[ResourceId]) { - log::debug!("gpu resources to_clean {:?}", to_clean); + log::trace!("gpu resources to_clean {to_clean:?}"); to_clean.iter().for_each(|res| match &res { ResourceId::Pipeline(id) => self.clean_pipeline(*id), ResourceId::Buffer(id) => self.clean_buffer(*id), @@ -579,7 +555,7 @@ impl DeviceBackend for GlowBackend { }); } - fn set_size(&mut self, width: i32, height: i32) { + fn set_size(&mut self, width: u32, height: u32) { self.size = (width, height); } @@ -611,7 +587,7 @@ impl DeviceBackend for GlowBackend { "Error creating render target: texture id '{texture_id}' not found.", ))?; - let inner_rt = InnerRenderTexture::new(&self.gl, texture, info)?; + let inner_rt = InnerRenderTexture::new(&self.gl, texture, texture_id, info)?; self.render_target_count += 1; self.render_targets .insert(self.render_target_count, inner_rt); @@ -644,13 +620,13 @@ impl DeviceBackend for GlowBackend { self.gl.tex_sub_image_2d( glow::TEXTURE_2D, 0, - opts.x_offset, - opts.y_offset, - opts.width, - opts.height, + opts.x_offset as _, + opts.y_offset as _, + opts.width as _, + opts.height as _, texture_format(&opts.format), texture_type(&opts.format), - PixelUnpackData::Slice(bytes), + PixelUnpackData::Slice(Some(bytes)), ); } TextureUpdaterSourceKind::Raw(source) => source.update(self, opts)?, @@ -692,19 +668,20 @@ impl DeviceBackend for GlowBackend { let can_read = status == glow::FRAMEBUFFER_COMPLETE; let clean = || { - self.gl.bind_framebuffer(glow::FRAMEBUFFER, self.default_gl_framebuffer); + self.gl + .bind_framebuffer(glow::FRAMEBUFFER, self.default_gl_framebuffer); self.gl.delete_framebuffer(fbo); }; if can_read { self.gl.read_pixels( - opts.x_offset, - opts.y_offset, - opts.width, - opts.height, + opts.x_offset as _, + opts.y_offset as _, + opts.width as _, + opts.height as _, texture_format(&opts.format), texture_type(&opts.format), - glow::PixelPackData::Slice(bytes), + glow::PixelPackData::Slice(Some(bytes)), ); clean(); diff --git a/crates/notan_glow/src/pipeline.rs b/crates/notan_glow/src/pipeline.rs index 940a23bf..24341789 100644 --- a/crates/notan_glow/src/pipeline.rs +++ b/crates/notan_glow/src/pipeline.rs @@ -86,6 +86,10 @@ impl InnerPipeline { set_color_mask(gl, options); set_culling(gl, options); set_blend_mode(gl, options); + #[cfg(not(target_arch = "wasm32"))] + set_srgb_space(gl, options); + #[cfg(not(target_arch = "wasm32"))] + set_point_size_available(gl, options); } } } @@ -243,6 +247,30 @@ unsafe fn set_blend_mode(gl: &Context, options: &PipelineOptions) { } } +#[inline(always)] +#[cfg(not(target_arch = "wasm32"))] +fn set_srgb_space(gl: &Context, opts: &PipelineOptions) { + unsafe { + if opts.srgb_space { + gl.enable(glow::FRAMEBUFFER_SRGB); + } else { + gl.disable(glow::FRAMEBUFFER_SRGB); + } + } +} + +#[inline(always)] +#[cfg(not(target_arch = "wasm32"))] +fn set_point_size_available(gl: &Context, opts: &PipelineOptions) { + unsafe { + if opts.point_size_available { + gl.enable(glow::VERTEX_PROGRAM_POINT_SIZE); + } else { + gl.disable(glow::VERTEX_PROGRAM_POINT_SIZE); + } + } +} + #[inline(always)] fn clean_pipeline(gl: &Context, pip: InnerPipeline) { let InnerPipeline { @@ -327,8 +355,12 @@ fn create_pipeline( #[cfg(debug_assertions)] { - for name in not_used_textures.iter() { - panic!("Wrong texture location id: {name}"); + let unused = not_used_textures + .iter() + .map(|name| format!("Wrong texture location id: {name}")) + .collect::>(); + if !unused.is_empty() { + panic!("{}", unused.join("\n")); } } diff --git a/crates/notan_glow/src/render_target.rs b/crates/notan_glow/src/render_target.rs index 11f476c2..123f98d1 100644 --- a/crates/notan_glow/src/render_target.rs +++ b/crates/notan_glow/src/render_target.rs @@ -6,12 +6,18 @@ use notan_graphics::prelude::*; pub(crate) struct InnerRenderTexture { fbo: Framebuffer, depth_texture: Option, - pub size: (i32, i32), + pub size: (u32, u32), pub use_mipmaps: bool, + pub texture_id: u64, } impl InnerRenderTexture { - pub fn new(gl: &Context, texture: &InnerTexture, info: &TextureInfo) -> Result { + pub fn new( + gl: &Context, + texture: &InnerTexture, + texture_id: u64, + info: &TextureInfo, + ) -> Result { let use_mipmaps = texture.use_mipmaps; let width = info.width; let height = info.height; @@ -28,6 +34,7 @@ impl InnerRenderTexture { depth_texture, size, use_mipmaps, + texture_id, }) } @@ -99,6 +106,6 @@ unsafe fn create_fbo( } struct DepthInfo { - width: i32, - height: i32, + width: u32, + height: u32, } diff --git a/crates/notan_glow/src/texture.rs b/crates/notan_glow/src/texture.rs index e0ff0414..6d284b44 100644 --- a/crates/notan_glow/src/texture.rs +++ b/crates/notan_glow/src/texture.rs @@ -6,20 +6,17 @@ pub(crate) type TextureKey = ::Texture; pub(crate) struct InnerTexture { pub texture: TextureKey, - pub size: (i32, i32), - pub is_srgba: bool, + pub size: (u32, u32), pub use_mipmaps: bool, } impl InnerTexture { pub fn new(texture: TextureKey, info: &TextureInfo) -> Result { let size = (info.width, info.height); - let is_srgba = info.format == TextureFormat::SRgba8; let use_mipmaps = info.mipmap_filter.is_some(); Ok(Self { texture, size, - is_srgba, use_mipmaps, }) } @@ -180,12 +177,12 @@ pub(crate) unsafe fn create_texture( glow::TEXTURE_2D, 0, texture_internal_format(&info.format) as _, - info.width, - info.height, + info.width as _, + info.height as _, 0, format, texture_type(&info.format), - data, + PixelUnpackData::Slice(data), ); post_create_texture(gl, info); @@ -206,6 +203,7 @@ pub(crate) fn texture_type(tf: &TextureFormat) -> u32 { pub(crate) fn texture_format(tf: &TextureFormat) -> u32 { match tf { + TextureFormat::Rgb24 => glow::RGB, TextureFormat::Rgba32 => glow::RGBA, TextureFormat::Rgba32Float => RGBA, TextureFormat::R8 => glow::RED, diff --git a/crates/notan_glow/src/texture_source.rs b/crates/notan_glow/src/texture_source.rs index a22566fa..336476d5 100644 --- a/crates/notan_glow/src/texture_source.rs +++ b/crates/notan_glow/src/texture_source.rs @@ -35,14 +35,12 @@ pub(crate) fn add_texture_from_image( } fn invalid_tex_size(max_size: u32, info: &TextureInfo) -> bool { - let w = info.width as u32; - let h = info.height as u32; - w > max_size || h > max_size + info.width > max_size || info.height > max_size } fn image_load_from_memory(buffer: &[u8]) -> Result { let format = image::guess_format(buffer).map_err(|e| e.to_string())?; - let mut reader = image::io::Reader::with_format(std::io::Cursor::new(buffer), format); + let mut reader = image::ImageReader::with_format(std::io::Cursor::new(buffer), format); // TODO allow to pass the limit from a config by the user reader.no_limits(); reader.decode().map_err(|e| e.to_string()) @@ -95,7 +93,7 @@ pub(crate) fn add_texture_from_bytes( ) -> Result<(u64, TextureInfo), String> { #[cfg(debug_assertions)] { - let size = info.width * info.height * (info.bytes_per_pixel() as i32); + let size = info.width * info.height * (info.bytes_per_pixel() as u32); debug_assert_eq!( bytes.len(), size as usize, diff --git a/crates/notan_glow/src/to_glow.rs b/crates/notan_glow/src/to_glow.rs index e37bdfda..873fe1ae 100644 --- a/crates/notan_glow/src/to_glow.rs +++ b/crates/notan_glow/src/to_glow.rs @@ -136,11 +136,11 @@ impl ToGlow for TextureFilter { impl ToGlow for DrawPrimitive { fn to_glow(&self) -> u32 { match self { + DrawPrimitive::Points => glow::POINTS, DrawPrimitive::Triangles => glow::TRIANGLES, DrawPrimitive::TriangleStrip => glow::TRIANGLE_STRIP, DrawPrimitive::Lines => glow::LINES, DrawPrimitive::LineStrip => glow::LINE_STRIP, - DrawPrimitive::Points => glow::POINTS, } } } diff --git a/crates/notan_glow/src/utils.rs b/crates/notan_glow/src/utils.rs index 09cb91d1..eaf88a59 100644 --- a/crates/notan_glow/src/utils.rs +++ b/crates/notan_glow/src/utils.rs @@ -17,11 +17,13 @@ pub(crate) fn create_gl_context( #[cfg(target_arch = "wasm32")] fn webgl_options(antialias: bool, transparent: bool) -> web_sys::WebGlContextAttributes { - let mut opts = web_sys::WebGlContextAttributes::new(); - opts.stencil(true); - opts.premultiplied_alpha(false); - opts.alpha(transparent); - opts.antialias(antialias); + let opts = web_sys::WebGlContextAttributes::new(); + opts.set_stencil(true); + opts.set_premultiplied_alpha(false); + opts.set_alpha(transparent); + opts.set_antialias(antialias); + opts.set_power_preference(web_sys::WebGlPowerPreference::HighPerformance); + opts.set_fail_if_major_performance_caveat(true); opts } @@ -31,13 +33,12 @@ fn create_webgl_context( antialias: bool, transparent: bool, ) -> Result { - //TODO manage errors let gl = win .get_context_with_context_options("webgl", webgl_options(antialias, transparent).as_ref()) - .unwrap() - .unwrap() + .map_err(|e| format!("{e:?}"))? + .ok_or("Cannot acquire the Webgl context. Is the canvas already instantiated?")? .dyn_into::() - .unwrap(); + .map_err(|_| "Cannot acquire WebGL context.")?; let ctx = glow::Context::from_webgl1_context(gl); Ok(ctx) @@ -49,13 +50,12 @@ fn create_webgl2_context( antialias: bool, transparent: bool, ) -> Result { - //TODO manage errors let gl = win .get_context_with_context_options("webgl2", webgl_options(antialias, transparent).as_ref()) - .unwrap() - .unwrap() + .map_err(|e| format!("{e:?}"))? + .ok_or("Cannot acquire the Webgl2 context. Is the canvas already instantiated?")? .dyn_into::() - .unwrap(); + .map_err(|_| "Cannot acquire WebGL2 context.")?; let ctx = glow::Context::from_webgl2_context(gl); Ok(ctx) diff --git a/crates/notan_glyph/Cargo.toml b/crates/notan_glyph/Cargo.toml index d8fb0e01..da07eaea 100644 --- a/crates/notan_glyph/Cargo.toml +++ b/crates/notan_glyph/Cargo.toml @@ -1,19 +1,27 @@ [package] name = "notan_glyph" -version = "0.9.5" -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides glyph's support for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -glyph_brush = "0.7.5" -bytemuck = "1.13.0" -notan_app = { path = "../notan_app", version = "0.9.5" } -notan_graphics = { path = "../notan_graphics", version = "0.9.5" } -notan_math = { path = "../notan_math", version = "0.9.5" } +notan_app.workspace = true +notan_graphics.workspace = true +notan_math.workspace = true +notan_macro.workspace = true + +log.workspace = true +bytemuck.workspace = true + +glyph_brush = "0.7.11" + +[features] +glsl-to-spirv = ["notan_macro/glsl-to-spirv"] +shaderc = ["notan_macro/shaderc"] diff --git a/crates/notan_glyph/src/lib.rs b/crates/notan_glyph/src/lib.rs index 9387789d..1ed0aec0 100644 --- a/crates/notan_glyph/src/lib.rs +++ b/crates/notan_glyph/src/lib.rs @@ -136,7 +136,7 @@ pub struct RenderQueueBuilder<'a, F = FontArc, H = DefaultSectionHasher> { pipeline: &'a mut dyn GlyphPipeline, clear: Option, region: Option, - size: Option<(i32, i32)>, + size: Option<(u32, u32)>, transform: Option, } @@ -177,7 +177,7 @@ impl<'a, F: Font + Sync, H: BuildHasher> RenderQueueBuilder<'a, F, H> { self } - pub fn size(mut self, width: i32, height: i32) -> Self { + pub fn size(mut self, width: u32, height: u32) -> Self { self.size = Some((width, height)); self } @@ -214,7 +214,7 @@ impl GlyphBrush { &'a mut self, device: &'a mut Device, pipeline: &'a mut dyn GlyphPipeline, - ) -> RenderQueueBuilder { + ) -> RenderQueueBuilder<'a, F, H> { RenderQueueBuilder::new(self, device, pipeline) } diff --git a/crates/notan_glyph/src/pipeline.rs b/crates/notan_glyph/src/pipeline.rs index c4136ea6..48877dbd 100644 --- a/crates/notan_glyph/src/pipeline.rs +++ b/crates/notan_glyph/src/pipeline.rs @@ -1,5 +1,6 @@ use crate::instance::GlyphInstance; use notan_app::graphics::*; +use notan_macro::*; use notan_math::Mat4; use notan_math::Rect; @@ -10,7 +11,7 @@ pub trait GlyphPipeline { texture: &Texture, clear: Option, transform: Mat4, - size: (i32, i32), + size: (u32, u32), region: Option, ) -> Renderer { let mut renderer = device.create_renderer(); @@ -34,7 +35,7 @@ pub trait GlyphPipeline { texture: &Texture, clear: Option, transform: Mat4, - size: (i32, i32), + size: (u32, u32), region: Option, ); @@ -166,7 +167,7 @@ impl GlyphPipeline for DefaultGlyphPipeline { texture: &Texture, clear: Option, transform: Mat4, - size: (i32, i32), + size: (u32, u32), region: Option, ) { if self.current_transform != transform { @@ -180,7 +181,7 @@ impl GlyphPipeline for DefaultGlyphPipeline { renderer.set_scissors(region.x, region.y, region.width, region.height); } - renderer.begin(clear.as_ref()); + renderer.begin(clear); renderer.set_pipeline(&self.pipeline); renderer.bind_texture(0, texture); renderer.bind_buffers(&[&self.vbo, &self.ubo]); diff --git a/crates/notan_graphics/Cargo.toml b/crates/notan_graphics/Cargo.toml index 976b9ca9..55250019 100644 --- a/crates/notan_graphics/Cargo.toml +++ b/crates/notan_graphics/Cargo.toml @@ -1,27 +1,30 @@ [package] name = "notan_graphics" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides simple graphics API for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bytemuck = "1.13.0" -parking_lot = "0.12.1" -image = { version = "0.24.5", default-features = false, features = ["jpeg", "png"] } -notan_math = { path = "../notan_math", version = "0.9.5" } -notan_macro = { path = "../notan_macro", version = "0.9.5" } -notan_utils = { path = "../notan_utils", version = "0.9.5" } -glsl-layout = { version = "0.5.0", features = ["glam"] } +notan_math.workspace = true +notan_utils.workspace = true + +log.workspace = true +bytemuck.workspace = true +parking_lot.workspace = true +image = { workspace = true, optional = true } + +crevice_notan = { version = "0.14.1" } +serde = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys = { version = "0.3.60" } [features] -texture_to_file = ["notan_utils/save_file"] +texture_to_file = ["notan_utils/save_file", "image/png"] diff --git a/crates/notan_graphics/src/buffer.rs b/crates/notan_graphics/src/buffer.rs index 9fb7cf8c..8f08339b 100644 --- a/crates/notan_graphics/src/buffer.rs +++ b/crates/notan_graphics/src/buffer.rs @@ -92,7 +92,7 @@ impl<'a> VertexBufferBuilder<'a> { } pub fn with_info(mut self, info: &VertexInfo) -> Self { - self.vertex_attrs = info.attrs.clone(); + self.vertex_attrs.clone_from(&info.attrs); self.vertex_step_mode = info.step_mode; self } diff --git a/crates/notan_graphics/src/color.rs b/crates/notan_graphics/src/color.rs index 78ba5ea1..5ea62890 100644 --- a/crates/notan_graphics/src/color.rs +++ b/crates/notan_graphics/src/color.rs @@ -1,5 +1,6 @@ /// Represents a visual color #[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Color { /// Red value pub r: f32, diff --git a/crates/notan_graphics/src/commands.rs b/crates/notan_graphics/src/commands.rs index dd41b34b..bc3f0180 100644 --- a/crates/notan_graphics/src/commands.rs +++ b/crates/notan_graphics/src/commands.rs @@ -5,8 +5,8 @@ use crate::pipeline::*; #[derive(Debug, Clone)] pub enum Commands { Size { - width: i32, - height: i32, + width: u32, + height: u32, }, Viewport { x: f32, diff --git a/crates/notan_graphics/src/device.rs b/crates/notan_graphics/src/device.rs index 7efa8d31..95e14c26 100644 --- a/crates/notan_graphics/src/device.rs +++ b/crates/notan_graphics/src/device.rs @@ -1,6 +1,6 @@ use crate::buffer::*; use crate::commands::*; -use crate::glsl_layout::{Std140, Uniform as UniformLayout}; +use crate::crevice::std140::{AsStd140, Std140}; use crate::limits::Limits; use crate::pipeline::*; use crate::render_texture::*; @@ -93,7 +93,7 @@ pub trait DeviceBackend { fn clean(&mut self, to_clean: &[ResourceId]); /// Sets the render size - fn set_size(&mut self, width: i32, height: i32); + fn set_size(&mut self, width: u32, height: u32); /// Sets the screen dpi fn set_dpi(&mut self, scale_factor: f64); @@ -146,7 +146,7 @@ impl DropManager { } pub struct Device { - size: (i32, i32), + size: (u32, u32), dpi: f64, backend: Box, //TODO generic? drop_manager: Arc, @@ -173,12 +173,12 @@ impl Device { } #[inline] - pub fn size(&self) -> (i32, i32) { + pub fn size(&self) -> (u32, u32) { self.size } #[inline] - pub fn set_size(&mut self, width: i32, height: i32) { + pub fn set_size(&mut self, width: u32, height: u32) { self.size = (width, height); self.backend.set_size(width, height); } @@ -206,49 +206,49 @@ impl Device { /// Creates a Pipeline builder #[inline] - pub fn create_pipeline(&mut self) -> PipelineBuilder { + pub fn create_pipeline(&mut self) -> PipelineBuilder<'_, '_> { PipelineBuilder::new(self) } /// Creates a texture builder #[inline] - pub fn create_texture(&mut self) -> TextureBuilder { + pub fn create_texture(&mut self) -> TextureBuilder<'_, '_> { TextureBuilder::new(self) } /// Creates a render texture builder #[inline] - pub fn create_render_texture(&mut self, width: i32, height: i32) -> RenderTextureBuilder { + pub fn create_render_texture(&mut self, width: u32, height: u32) -> RenderTextureBuilder<'_> { RenderTextureBuilder::new(self, width, height) } /// Creates a vertex buffer builder #[inline] - pub fn create_vertex_buffer(&mut self) -> VertexBufferBuilder { + pub fn create_vertex_buffer(&mut self) -> VertexBufferBuilder<'_> { VertexBufferBuilder::new(self) } /// Creates a index buffer builder #[inline] - pub fn create_index_buffer(&mut self) -> IndexBufferBuilder { + pub fn create_index_buffer(&mut self) -> IndexBufferBuilder<'_> { IndexBufferBuilder::new(self) } /// Creates a uniform buffer builder #[inline] - pub fn create_uniform_buffer(&mut self, slot: u32, name: &str) -> UniformBufferBuilder { + pub fn create_uniform_buffer(&mut self, slot: u32, name: &str) -> UniformBufferBuilder<'_> { UniformBufferBuilder::new(self, slot, name) } /// Update the texture data #[inline] - pub fn update_texture<'a>(&'a mut self, texture: &'a mut Texture) -> TextureUpdater { + pub fn update_texture<'a>(&'a mut self, texture: &'a mut Texture) -> TextureUpdater<'a> { TextureUpdater::new(self, texture) } /// Read pixels from a texture #[inline] - pub fn read_pixels<'a>(&'a mut self, texture: &'a Texture) -> TextureReader { + pub fn read_pixels<'a>(&'a mut self, texture: &'a Texture) -> TextureReader<'a> { TextureReader::new(self, texture) } @@ -291,12 +291,20 @@ impl Device { options: PipelineOptions, ) -> Result { let api = self.backend.api_name(); - let vertex = vertex_source - .get_source(api) - .ok_or(format!("Vertex shader for api '{api}' not available."))?; - let fragment = fragment_source - .get_source(api) - .ok_or(format!("Fragment shader for api '{api}' not available."))?; + let vertex = match vertex_source.get_source(api) { + Some(v) => v, + None => { + log::warn!("Vertex shader for api '{api}' not available."); + &[] + } + }; + let fragment = match fragment_source.get_source(api) { + Some(f) => f, + None => { + log::warn!("Fragment shader for api '{api}' not available."); + &[] + } + }; self.inner_create_pipeline_from_raw( vertex, fragment, @@ -458,7 +466,7 @@ impl Device { } } -pub trait Uniform: UniformLayout {} +pub trait Uniform: AsStd140 {} pub trait BufferData { fn upload(&self, device: &mut Device, id: u64); fn save_as_bytes(&self, _data: &mut Vec) {} @@ -519,11 +527,13 @@ where #[inline] fn upload(&self, device: &mut Device, id: u64) { // TODO check opengl version or driver if it uses std140 to layout or not - device.backend.set_buffer_data(id, self.std140().as_raw()); + device + .backend + .set_buffer_data(id, self.as_std140().as_bytes()); } fn save_as_bytes(&self, data: &mut Vec) { - data.extend_from_slice(self.std140().as_raw()); + data.extend_from_slice(self.as_std140().as_bytes()); } } diff --git a/crates/notan_graphics/src/lib.rs b/crates/notan_graphics/src/lib.rs index b109db85..80b8ea75 100644 --- a/crates/notan_graphics/src/lib.rs +++ b/crates/notan_graphics/src/lib.rs @@ -11,7 +11,7 @@ pub mod texture; pub mod prelude; -pub use glsl_layout; +pub use crevice_notan as crevice; #[cfg(feature = "texture_to_file")] mod to_file; @@ -22,7 +22,3 @@ pub use render_texture::*; pub use renderer::*; pub use shader::*; pub use texture::*; - -pub use notan_macro::{ - fragment_shader, include_fragment_shader, include_vertex_shader, vertex_shader, -}; diff --git a/crates/notan_graphics/src/pipeline.rs b/crates/notan_graphics/src/pipeline.rs index 20f00c9a..bb63d2ac 100644 --- a/crates/notan_graphics/src/pipeline.rs +++ b/crates/notan_graphics/src/pipeline.rs @@ -156,6 +156,18 @@ impl<'a, 'b> PipelineBuilder<'a, 'b> { self } + /// Enable the SRGB Color Space + pub fn with_srgb_space(mut self, srgb: bool) -> Self { + self.options.srgb_space = srgb; + self + } + + /// Enable the availability of gl_PointSize in the vertex shader + pub fn with_point_size_available(mut self, enabled: bool) -> Self { + self.options.point_size_available = enabled; + self + } + /// Build the pipeline with the data set on the builder pub fn build(self) -> Result { match self.shaders { @@ -351,6 +363,8 @@ pub struct PipelineOptions { pub depth_stencil: DepthStencil, pub color_mask: ColorMask, pub stencil: Option, + pub srgb_space: bool, + pub point_size_available: bool, } impl Default for PipelineOptions { @@ -362,6 +376,8 @@ impl Default for PipelineOptions { alpha_blend: None, color_mask: Default::default(), stencil: None, + srgb_space: false, + point_size_available: true, } } } @@ -436,9 +452,9 @@ impl Default for StencilOptions { #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub enum DrawPrimitive { + Points, Lines, LineStrip, - Points, #[default] Triangles, TriangleStrip, diff --git a/crates/notan_graphics/src/prelude.rs b/crates/notan_graphics/src/prelude.rs index c3da85dc..c16b6fa9 100644 --- a/crates/notan_graphics/src/prelude.rs +++ b/crates/notan_graphics/src/prelude.rs @@ -2,14 +2,9 @@ pub use crate::buffer::*; pub use crate::color::*; pub use crate::commands::*; pub use crate::device::*; -pub use crate::glsl_layout; pub use crate::limits::*; pub use crate::pipeline::*; pub use crate::render_texture::*; pub use crate::renderer::*; pub use crate::shader::*; pub use crate::texture::*; - -pub use notan_macro::{ - fragment_shader, include_fragment_shader, include_vertex_shader, vertex_shader, -}; diff --git a/crates/notan_graphics/src/render_texture.rs b/crates/notan_graphics/src/render_texture.rs index d12db9d9..545ecf5f 100644 --- a/crates/notan_graphics/src/render_texture.rs +++ b/crates/notan_graphics/src/render_texture.rs @@ -80,7 +80,7 @@ pub struct RenderTextureBuilder<'a> { } impl<'a> RenderTextureBuilder<'a> { - pub fn new(device: &'a mut Device, width: i32, height: i32) -> Self { + pub fn new(device: &'a mut Device, width: u32, height: u32) -> Self { let info = TextureInfo { width, height, @@ -109,6 +109,29 @@ impl<'a> RenderTextureBuilder<'a> { self } + /// Set the texture wrap modes (x -> s, y -> t) + pub fn with_wrap(mut self, x: TextureWrap, y: TextureWrap) -> Self { + self.info.wrap_x = x; + self.info.wrap_y = y; + self + } + + /// Toggle mipmap generation (with Linear filter if enabled) + pub fn with_mipmaps(mut self, enable: bool) -> Self { + if enable { + self.info.mipmap_filter = Some(TextureFilter::Linear); + } else { + self.info.mipmap_filter = None; + } + self + } + + /// Set mipmap filtering function + pub fn with_mipmap_filter(mut self, filter: TextureFilter) -> Self { + self.info.mipmap_filter = Some(filter); + self + } + pub fn build(self) -> Result { let Self { device, info } = self; diff --git a/crates/notan_graphics/src/renderer.rs b/crates/notan_graphics/src/renderer.rs index 807f1459..bdeb76db 100644 --- a/crates/notan_graphics/src/renderer.rs +++ b/crates/notan_graphics/src/renderer.rs @@ -7,13 +7,13 @@ use crate::texture::*; #[derive(Default, Clone)] pub struct Renderer { commands: Vec, - size: (i32, i32), + size: (u32, u32), primitive: DrawPrimitive, slot_count: u32, } impl Renderer { - pub fn new(width: i32, height: i32) -> Self { + pub fn new(width: u32, height: u32) -> Self { Self { size: (width, height), commands: vec![Commands::Size { width, height }], @@ -22,7 +22,7 @@ impl Renderer { } } - pub fn begin(&mut self, options: Option<&ClearOptions>) { + pub fn begin(&mut self, options: Option) { let (color, stencil, depth) = match options { Some(opts) => (opts.color, opts.stencil, opts.depth), _ => (None, None, None), @@ -44,20 +44,20 @@ impl Renderer { self.unbind_textures(); } - pub fn set_size(&mut self, width: i32, height: i32) { + pub fn set_size(&mut self, width: u32, height: u32) { self.size = (width, height); self.commands.push(Commands::Size { width, height }); } - pub fn size(&self) -> (i32, i32) { + pub fn size(&self) -> (u32, u32) { self.size } - pub fn width(&self) -> i32 { + pub fn width(&self) -> u32 { self.size.0 } - pub fn height(&self) -> i32 { + pub fn height(&self) -> u32 { self.size.1 } diff --git a/crates/notan_graphics/src/shader.rs b/crates/notan_graphics/src/shader.rs index 68a22b4d..ae0eec43 100644 --- a/crates/notan_graphics/src/shader.rs +++ b/crates/notan_graphics/src/shader.rs @@ -3,7 +3,7 @@ pub struct ShaderSource<'a> { pub sources: &'a [(&'a str, &'a [u8])], } -impl<'a> ShaderSource<'a> { +impl ShaderSource<'_> { pub fn get_source(&self, api: &str) -> Option<&[u8]> { self.sources .iter() diff --git a/crates/notan_graphics/src/texture.rs b/crates/notan_graphics/src/texture.rs index 977daf7e..c97a2e19 100644 --- a/crates/notan_graphics/src/texture.rs +++ b/crates/notan_graphics/src/texture.rs @@ -18,26 +18,26 @@ pub trait TextureSource { #[derive(Debug)] pub struct TextureRead { - pub x_offset: i32, - pub y_offset: i32, - pub width: i32, - pub height: i32, + pub x_offset: u32, + pub y_offset: u32, + pub width: u32, + pub height: u32, pub format: TextureFormat, } #[derive(Debug, Clone)] pub struct TextureUpdate { - pub x_offset: i32, - pub y_offset: i32, - pub width: i32, - pub height: i32, + pub x_offset: u32, + pub y_offset: u32, + pub width: u32, + pub height: u32, pub format: TextureFormat, } #[derive(Debug, Clone)] pub struct TextureInfo { - pub width: i32, - pub height: i32, + pub width: u32, + pub height: u32, pub format: TextureFormat, pub min_filter: TextureFilter, pub mag_filter: TextureFilter, @@ -81,13 +81,13 @@ impl TextureFormat { R8 => 1, R8Uint => 1, R16Uint => 2, + Rgb24 => 3, Rgba32Float => 4 * 4, _ => 4, } } } -#[derive(Debug)] struct TextureIdRef { id: u64, drop_manager: Arc, @@ -99,12 +99,18 @@ impl Drop for TextureIdRef { } } +impl Debug for TextureIdRef { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TextureIdRef({})", self.id) + } +} + #[derive(Debug, Clone)] pub struct Texture { id: u64, _id_ref: Arc, - width: i32, - height: i32, + width: u32, + height: u32, format: TextureFormat, min_filter: TextureFilter, mag_filter: TextureFilter, @@ -244,6 +250,7 @@ impl AsRef for Texture { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum TextureFormat { SRgba8, + Rgb24, Rgba32, R8, R8Uint, @@ -330,7 +337,7 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { } /// Creates a Texture from a buffer of pixels - pub fn from_bytes(mut self, bytes: &'b [u8], width: i32, height: i32) -> Self { + pub fn from_bytes(mut self, bytes: &'b [u8], width: u32, height: u32) -> Self { self.source = None; self.kind = Some(TextureKind::Bytes(bytes)); self.info.width = width; @@ -339,14 +346,14 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { } /// Creates a buffer for the size passed in and creates a Texture with it - pub fn from_empty_buffer(mut self, width: i32, height: i32) -> Self { + pub fn from_empty_buffer(mut self, width: u32, height: u32) -> Self { self.source = None; self.kind = Some(TextureKind::EmptyBuffer); self.with_size(width, height) } /// Set the size of the texture (ignored if used with `from_image`, image size will be used instead) - pub fn with_size(mut self, width: i32, height: i32) -> Self { + pub fn with_size(mut self, width: u32, height: u32) -> Self { self.info.width = width; self.info.height = height; self @@ -384,7 +391,7 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { self } - /// Generate the mipmaps + /// Toggle mipmap generation (with Linear filter if enabled) pub fn with_mipmaps(mut self, enable: bool) -> Self { if enable { self.info.mipmap_filter = Some(TextureFilter::Linear); @@ -394,6 +401,7 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { self } + /// Set mipmap filtering function pub fn with_mipmap_filter(mut self, filter: TextureFilter) -> Self { self.info.mipmap_filter = Some(filter); self @@ -412,7 +420,7 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { source = Some(TextureSourceKind::Image(bytes.to_vec())); } Some(TextureKind::Bytes(bytes)) => { - let size = (info.width * info.height * (info.bytes_per_pixel() as i32)) as usize; + let size = (info.width * info.height * (info.bytes_per_pixel() as u32)) as usize; if bytes.len() != size { return Err(format!( "Texture type {:?} with {} bytes, when it should be {} (width: {} * height: {} * bytes: {})", @@ -428,7 +436,7 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { source = Some(TextureSourceKind::Bytes(bytes.to_vec())); } Some(TextureKind::EmptyBuffer) => { - let size = info.width * info.height * (info.bytes_per_pixel() as i32); + let size = info.width * info.height * (info.bytes_per_pixel() as u32); source = Some(TextureSourceKind::Bytes(vec![0; size as _])); } None => {} @@ -442,20 +450,20 @@ impl<'a, 'b> TextureBuilder<'a, 'b> { pub struct TextureReader<'a> { device: &'a mut Device, texture: &'a Texture, - x_offset: i32, - y_offset: i32, - width: i32, - height: i32, + x_offset: u32, + y_offset: u32, + width: u32, + height: u32, format: TextureFormat, } impl<'a> TextureReader<'a> { pub fn new(device: &'a mut Device, texture: &'a Texture) -> Self { let rect = *texture.frame(); - let x_offset = rect.x as i32; - let y_offset = rect.y as i32; - let width = rect.width as i32; - let height = rect.height as i32; + let x_offset = rect.x as _; + let y_offset = rect.y as _; + let width = rect.width as _; + let height = rect.height as _; let format = texture.format; Self { device, @@ -469,25 +477,25 @@ impl<'a> TextureReader<'a> { } /// Read pixels from the axis x offset - pub fn with_x_offset(mut self, offset: i32) -> Self { + pub fn with_x_offset(mut self, offset: u32) -> Self { self.x_offset = offset; self } /// Read pixels from the axis y offset - pub fn with_y_offset(mut self, offset: i32) -> Self { + pub fn with_y_offset(mut self, offset: u32) -> Self { self.y_offset = offset; self } /// Read pixels until this width from the x offset value - pub fn with_width(mut self, width: i32) -> Self { + pub fn with_width(mut self, width: u32) -> Self { self.width = width; self } /// Read pixels until this height from the y offset value - pub fn with_height(mut self, height: i32) -> Self { + pub fn with_height(mut self, height: u32) -> Self { self.height = height; self } @@ -518,10 +526,10 @@ impl<'a> TextureReader<'a> { pub struct TextureUpdater<'a> { device: &'a mut Device, texture: &'a mut Texture, - x_offset: i32, - y_offset: i32, - width: i32, - height: i32, + x_offset: u32, + y_offset: u32, + width: u32, + height: u32, format: TextureFormat, source: Option>, } @@ -547,25 +555,25 @@ impl<'a> TextureUpdater<'a> { } /// Update pixels from the axis x offset - pub fn with_x_offset(mut self, offset: i32) -> Self { + pub fn with_x_offset(mut self, offset: u32) -> Self { self.x_offset = offset; self } /// Update pixels from the axis y offset - pub fn with_y_offset(mut self, offset: i32) -> Self { + pub fn with_y_offset(mut self, offset: u32) -> Self { self.y_offset = offset; self } /// Update pixels until this width from the x offset value - pub fn with_width(mut self, width: i32) -> Self { + pub fn with_width(mut self, width: u32) -> Self { self.width = width; self } /// Update pixels until this height from the y offset value - pub fn with_height(mut self, height: i32) -> Self { + pub fn with_height(mut self, height: u32) -> Self { self.height = height; self } diff --git a/crates/notan_graphics/src/to_file.rs b/crates/notan_graphics/src/to_file.rs index f4535a86..5d0f6858 100644 --- a/crates/notan_graphics/src/to_file.rs +++ b/crates/notan_graphics/src/to_file.rs @@ -1,4 +1,3 @@ -#![cfg(feature = "texture_to_file")] use crate::Device; use crate::Texture; use image::ColorType; @@ -36,7 +35,7 @@ pub(crate) fn save_to_png_file>( let mut data = vec![]; let encoder = image::codecs::png::PngEncoder::new(&mut data); encoder - .write_image(&bytes, width as _, height as _, typ) + .write_image(&bytes, width as _, height as _, typ.into()) .map_err(|e| e.to_string())?; save_file(p, &data) diff --git a/crates/notan_input/Cargo.toml b/crates/notan_input/Cargo.toml index dc6341e9..2c84c293 100644 --- a/crates/notan_input/Cargo.toml +++ b/crates/notan_input/Cargo.toml @@ -1,20 +1,21 @@ [package] name = "notan_input" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a set of API to manage user's input" [dependencies] -notan_core = { path = "../notan_core", version = "0.9.5" } -notan_math = { path = "../notan_math", version = "0.9.5" } -hashbrown = "0.13.2" -log = "0.4.17" -serde = { version = "1", optional = true, features = ["serde_derive"] } +notan_core.workspace = true +notan_math.workspace = true + +hashbrown.workspace = true +log.workspace = true +serde = { workspace = true, optional = true } [features] -serde = ["dep:serde", "notan_core/serde", "notan_math/serde", "hashbrown/serde"] \ No newline at end of file +serde = ["dep:serde", "notan_core/serde", "notan_math/serde", "hashbrown/serde"] diff --git a/crates/notan_input/src/keyboard.rs b/crates/notan_input/src/keyboard.rs index 2949dce5..a481f777 100644 --- a/crates/notan_input/src/keyboard.rs +++ b/crates/notan_input/src/keyboard.rs @@ -46,25 +46,25 @@ impl Keyboard { #[inline] /// returns true if any control key is down pub fn ctrl(&self) -> bool { - self.is_down(KeyCode::RControl) || self.is_down(KeyCode::LControl) + self.is_down(KeyCode::ControlRight) || self.is_down(KeyCode::ControlLeft) } #[inline] /// returns true if any alt key is down pub fn alt(&self) -> bool { - self.is_down(KeyCode::RAlt) || self.is_down(KeyCode::LAlt) + self.is_down(KeyCode::AltRight) || self.is_down(KeyCode::AltLeft) } #[inline] /// returns true if any shift key is down pub fn shift(&self) -> bool { - self.is_down(KeyCode::RShift) || self.is_down(KeyCode::LShift) + self.is_down(KeyCode::ShiftRight) || self.is_down(KeyCode::ShiftLeft) } #[inline] /// returns true if any logo (win or command) key is down pub fn logo(&self) -> bool { - self.is_down(KeyCode::RWin) || self.is_down(KeyCode::LWin) + self.is_down(KeyCode::SuperRight) || self.is_down(KeyCode::SuperLeft) } pub(crate) fn clear(&mut self) { diff --git a/crates/notan_input/src/mouse.rs b/crates/notan_input/src/mouse.rs index 20307c6d..8e446de8 100644 --- a/crates/notan_input/src/mouse.rs +++ b/crates/notan_input/src/mouse.rs @@ -2,7 +2,7 @@ use hashbrown::{HashMap, HashSet}; use notan_core::events::Event; pub use notan_core::mouse::MouseButton; -use notan_math::{Mat3, Vec2}; +use notan_math::Vec2; #[derive(Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,6 +20,12 @@ pub struct Mouse { pub released: HashSet, /// wheel delta pub wheel_delta: Vec2, + /// motion delta + pub motion_delta: (f64, f64), + /// used internally to reset the wheel_delta + scrolling: bool, + /// used internally to reset the motion_delta + moving: bool, } impl Mouse { @@ -33,16 +39,6 @@ impl Mouse { (self.x, self.y) } - #[inline] - #[doc(hidden)] - #[deprecated] - #[allow(deprecated)] - /// Returns a local position - pub fn local_position(&self, m: Mat3) -> (f32, f32) { - let pos = notan_math::mat3_screen_to_local(self.x, self.y, m); - (pos.x, pos.y) - } - #[inline] /// Returns true if the button was released on the last frame pub fn was_released(&self, btn: MouseButton) -> bool { @@ -121,17 +117,48 @@ impl Mouse { self.was_pressed(MouseButton::Right) } + #[inline(always)] + /// Returns true if the user is scrolling in this frame + pub fn is_scrolling(&self) -> bool { + self.scrolling + } + + #[inline(always)] + /// Returns true if the user is moving the mouse in this frame + pub fn is_moving(&self) -> bool { + self.moving + } + + /// Cleans the state of a button (useful for stop propagation) + pub fn clean_button_state(&mut self, btn: MouseButton) { + self.pressed.remove(&btn); + self.down.remove(&btn); + self.released.remove(&btn); + } + #[inline] pub(crate) fn clear(&mut self) { self.pressed.clear(); self.released.clear(); + + if !self.scrolling { + self.wheel_delta.x = 0.0; + self.wheel_delta.y = 0.0; + } + + if !self.moving { + self.motion_delta.0 = 0.0; + self.motion_delta.1 = 0.0; + } + + // we set it to false after the check to reset the wheel_delta to keep the value for at + // least one frame, and if the next frame we're not scrolling then we reset wheel_delta + self.scrolling = false; + self.moving = false; } #[inline] pub(crate) fn process_events(&mut self, evt: &Event, delta: f32) { - self.wheel_delta.x = 0.0; - self.wheel_delta.y = 0.0; - match evt { Event::MouseMove { x, y } => { self.x = *x as f32; @@ -151,16 +178,22 @@ impl Mouse { self.x = *x as f32; self.y = *y as f32; - if let Some(t) = self.down.get_mut(button) { - *t += delta; - } else { - self.down.insert(*button, 0.0); - self.pressed.insert(*button); + match self.down.get_mut(button) { + Some(t) => *t += delta, + None => { + self.down.insert(*button, 0.0); + self.pressed.insert(*button); + } } } Event::MouseWheel { delta_x, delta_y } => { self.wheel_delta.x = *delta_x; self.wheel_delta.y = *delta_y; + self.scrolling = true; + } + Event::MouseMotion { delta } => { + self.motion_delta = *delta; + self.moving = true; } _ => {} } diff --git a/crates/notan_log/Cargo.toml b/crates/notan_log/Cargo.toml index afeaa68f..ee46bed2 100644 --- a/crates/notan_log/Cargo.toml +++ b/crates/notan_log/Cargo.toml @@ -1,25 +1,28 @@ [package] name = "notan_log" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a multipatform log support for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -fern = { version = "0.6.1", features = ["colored"] } -notan_app = { path = "../notan_app", version = "0.9.5" } +notan_app.workspace = true + +log.workspace = true + +fern = { version = "0.7.1", features = ["colored"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -time = { version = "0.3.17", features = ["formatting", "local-offset"] } +time = { version = "0.3.37", features = ["formatting", "local-offset"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -console_log = "0.2.0" -wasm-bindgen = "0.2.83" -js-sys = "0.3.60" +wasm-bindgen.workspace = true +js-sys.workspace = true + +console_log = "1.0.0" diff --git a/crates/notan_log/src/config.rs b/crates/notan_log/src/config.rs index 5e7b2db9..8fbac18e 100644 --- a/crates/notan_log/src/config.rs +++ b/crates/notan_log/src/config.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use crate::console_error::console_error; /// Configure the logs output -/// Logs will show a timestamp using the UTC time with format [year]-[month]-[day] [hour]:[minutes]:[seconds] +/// Logs will show a timestamp using the UTC time with format `[year]-[month]-[day] [hour]:[minutes]:[seconds]` #[derive(Clone)] pub struct LogConfig { level: log::LevelFilter, diff --git a/crates/notan_log/src/console_error.rs b/crates/notan_log/src/console_error.rs index 38b82cb1..7440e4de 100644 --- a/crates/notan_log/src/console_error.rs +++ b/crates/notan_log/src/console_error.rs @@ -1,4 +1,3 @@ -#![cfg(target_arch = "wasm32")] #![allow(clippy::unused_unit)] use wasm_bindgen::prelude::*; diff --git a/crates/notan_macro/Cargo.toml b/crates/notan_macro/Cargo.toml index 795035ca..6c2ad772 100644 --- a/crates/notan_macro/Cargo.toml +++ b/crates/notan_macro/Cargo.toml @@ -1,27 +1,27 @@ [package] name = "notan_macro" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a set of utils as macros for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -syn = { version = "1.0.107", features = ["full", "extra-traits"] } -quote = "1.0.23" -num = "0.4.0" +syn = { version = "2.0.98", features = ["full", "extra-traits"] } +quote = "1.0.38" +num = "0.4.3" glsl-to-spirv = { version = "0.1.7", optional = true } -shaderc = { version = "0.8.2", optional = true } -proc-macro2 = "1.0.50" +shaderc = { version = "0.10.1", optional = true } +proc-macro2 = "1.0.93" spirv_cross = { version = "0.23.1", features = ["glsl"] } [build-dependencies] -cfg_aliases = "0.1.1" +cfg_aliases = "0.2.1" [features] glsl-to-spirv = ["dep:glsl-to-spirv"] @@ -29,4 +29,3 @@ shaderc = ["dep:shaderc"] [lib] proc-macro = true - diff --git a/crates/notan_macro/build.rs b/crates/notan_macro/build.rs index fab2958c..9d888a0d 100644 --- a/crates/notan_macro/build.rs +++ b/crates/notan_macro/build.rs @@ -1,14 +1,9 @@ use cfg_aliases::cfg_aliases; fn main() { - // We're defining features here to make it easy to swap between - // naga, glsl-to-spirv and shaderc keeping a priority order - // without emit compile errors that can mess with compilations - // wit the --all-features flag enabled. - - // TODO: add naga once the PR lands cfg_aliases! { - use_glsl_to_spirv: { all(feature = "glsl_to_spirv", not(feature = "shaderc")) }, - use_shaderc: { feature = "shaderc" } + use_shaderc: { feature = "shaderc" }, + use_glsl_to_spirv: { all(feature = "glsl-to-spirv", not(feature = "shaderc")) }, + shader_compilation: { any(use_shaderc, use_glsl_to_spirv) } } } diff --git a/crates/notan_macro/src/handlers.rs b/crates/notan_macro/src/handlers.rs index ed55051e..7f6cca43 100644 --- a/crates/notan_macro/src/handlers.rs +++ b/crates/notan_macro/src/handlers.rs @@ -124,7 +124,7 @@ fn enum_impl_generator(tokens: &Tokens, once: bool) -> String { .ret .as_ref() .map(|v| format!(" -> {v}")) - .unwrap_or_else(|| "".to_string()); + .unwrap_or_default(); let callback = enum_callback_generics(&combo(&tokens.params), &tokens.params); let reference = if once { "" } else { "&" }; @@ -171,7 +171,7 @@ fn trait_impl_generator(tokens: &Tokens, gen_type: GenericType, fn_literal: &str .ret .as_ref() .map(|v| format!(" -> {v}")) - .unwrap_or_else(|| "".to_string()); + .unwrap_or_default(); let s_type = match gen_type { GenericType::Plugin => "Plugin + 'static", @@ -252,8 +252,7 @@ fn enum_generics(g: &[Vec], r: Option<&String>, fn_literal: &str) -> Str "_{}(Box)", i, gen, - r.map(|v| format!(" -> {v}")) - .unwrap_or_else(|| "".to_string()) + r.map(|v| format!(" -> {v}")).unwrap_or_default() ) }) .collect::>() diff --git a/crates/notan_macro/src/lib.rs b/crates/notan_macro/src/lib.rs index 3bb28b44..4bfdcd80 100644 --- a/crates/notan_macro/src/lib.rs +++ b/crates/notan_macro/src/lib.rs @@ -1,10 +1,13 @@ extern crate proc_macro; use proc_macro::*; use quote::quote; -use syn::{parse_macro_input, DeriveInput, LitStr}; +use syn::DeriveInput; +#[cfg(shader_compilation)] +use syn::{parse_macro_input, LitStr}; use syn::{ItemFn, ReturnType}; mod handlers; +#[cfg(shader_compilation)] mod shaders; mod state; @@ -15,7 +18,7 @@ pub fn notan_main(_attr: TokenStream, item: TokenStream) -> TokenStream { } fn handle_main_func(input: ItemFn) -> TokenStream { - let ident = input.sig.ident.clone(); + let ident = &input.sig.ident; let void_ret = input.sig.output == ReturnType::Default; let ret: proc_macro2::TokenStream = if void_ret { "()" } else { "Result<(), String>" } .parse() @@ -63,6 +66,7 @@ pub fn state_derive(input: TokenStream) -> TokenStream { state::impl_state_derive(&ast) } +#[cfg(shader_compilation)] #[proc_macro] pub fn vertex_shader(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as LitStr); @@ -72,6 +76,7 @@ pub fn vertex_shader(input: TokenStream) -> TokenStream { shaders::source_from_spirv(spirv).unwrap() } +#[cfg(shader_compilation)] #[proc_macro] pub fn include_vertex_shader(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as LitStr); @@ -81,6 +86,7 @@ pub fn include_vertex_shader(input: TokenStream) -> TokenStream { shaders::source_from_spirv(spirv).unwrap() } +#[cfg(shader_compilation)] #[proc_macro] pub fn fragment_shader(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as LitStr); @@ -90,6 +96,7 @@ pub fn fragment_shader(input: TokenStream) -> TokenStream { shaders::source_from_spirv(spirv).unwrap() } +#[cfg(shader_compilation)] #[proc_macro] pub fn include_fragment_shader(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as LitStr); @@ -105,7 +112,7 @@ pub fn uniform(_metadata: TokenStream, input: TokenStream) -> TokenStream { let ident = derive.ident; let input: proc_macro2::TokenStream = input.into(); let output = quote! { - #[derive(glsl_layout::Uniform)] + #[derive(::notan::graphics::crevice::std140::AsStd140)] #input impl ::notan::graphics::Uniform for #ident {} diff --git a/crates/notan_macro/src/shaders.rs b/crates/notan_macro/src/shaders.rs index 4d4171e6..ec3c9140 100644 --- a/crates/notan_macro/src/shaders.rs +++ b/crates/notan_macro/src/shaders.rs @@ -154,7 +154,6 @@ impl quote::ToTokens for ShaderBytes { pub(crate) fn source_from_spirv(spirv: Vec) -> Result { let webgl2_bytes = spirv_to(&spirv, Output::Webgl2)?; - // let wgpu_bytes = spirv_to(&spirv, Output::Wgpu)?; let opengl_3_3_bytes = spirv_to(&spirv, Output::OpenGl3_3)?; let opengl_es_bytes = spirv_to(&spirv, Output::OpenGl_ES)?; @@ -164,10 +163,7 @@ pub(crate) fn source_from_spirv(spirv: Vec) -> Result { #[cfg(target_arch = "wasm32")] ("webgl2", &#webgl2_bytes), - // #[cfg(all(not(target_arch = "wasm32"), feature = "wgpu"))] - // ("wgpu", &#wgpu_bytes), - - #[cfg(all(not(target_arch = "wasm32"), not(feature = "wgpu"), not(target_os = "ios")))] + #[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "android")))] ("opengl", &#opengl_3_3_bytes), #[cfg(any(target_os = "ios", target_os = "android"))] @@ -267,7 +263,7 @@ pub fn read_spirv(mut x: R) -> io::Result> { "input length not divisible by 4", )); } - if size > usize::max_value() as u64 { + if size > usize::MAX as u64 { return Err(io::Error::new(io::ErrorKind::InvalidData, "input too long")); } let words = (size / 4) as usize; diff --git a/crates/notan_macro/src/state.rs b/crates/notan_macro/src/state.rs index dd78f965..150dff03 100644 --- a/crates/notan_macro/src/state.rs +++ b/crates/notan_macro/src/state.rs @@ -4,8 +4,11 @@ use quote::quote; pub(crate) fn impl_state_derive(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; + let generics = &ast.generics; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let gen = quote! { - impl notan::app::AppState for #name {} + impl #impl_generics notan::app::AppState for #name #ty_generics #where_clause {} }; gen.into() } diff --git a/crates/notan_math/Cargo.toml b/crates/notan_math/Cargo.toml index 28a69668..21274031 100644 --- a/crates/notan_math/Cargo.toml +++ b/crates/notan_math/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "notan_math" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides simple set of math's utils for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -glam = { version = "0.22.0", features = ["scalar-math", "bytemuck"] } -serde = { version = "1", optional = true, features = ["serde_derive"] } +glam = { version = "0.29", features = ["bytemuck"] } +serde = { workspace = true, optional = true } [features] serde = ["dep:serde", "glam/serde"] diff --git a/crates/notan_math/src/lib.rs b/crates/notan_math/src/lib.rs index 81a9fe91..57637052 100644 --- a/crates/notan_math/src/lib.rs +++ b/crates/notan_math/src/lib.rs @@ -1,7 +1,4 @@ mod rect; -mod utils; - -pub use rect::*; pub use glam::*; -pub use utils::*; +pub use rect::*; diff --git a/crates/notan_math/src/rect.rs b/crates/notan_math/src/rect.rs index 211d0372..bcf68581 100644 --- a/crates/notan_math/src/rect.rs +++ b/crates/notan_math/src/rect.rs @@ -31,4 +31,8 @@ impl Rect { pub fn center_y(&self) -> f32 { self.y + self.height * 0.5 } + + pub fn contains(&self, x: f32, y: f32) -> bool { + x >= self.min_x() && x <= self.max_x() && y >= self.min_y() && y <= self.max_y() + } } diff --git a/crates/notan_math/src/utils.rs b/crates/notan_math/src/utils.rs deleted file mode 100644 index e78569c8..00000000 --- a/crates/notan_math/src/utils.rs +++ /dev/null @@ -1,28 +0,0 @@ -/// Returns a local position from screen -#[inline] -#[deprecated] -#[doc(hidden)] -pub fn mat3_screen_to_local(x: f32, y: f32, m: glam::Mat3) -> glam::Vec2 { - let inverse = m.inverse(); - let v = inverse * glam::vec3(x, y, 1.0); - glam::vec2(v.x, v.y) -} - -/// Returns a screen position from local -#[inline] -#[deprecated] -#[doc(hidden)] -pub fn mat3_local_to_screen(x: f32, y: f32, m: glam::Mat3) -> glam::Vec2 { - let v = m * glam::vec3(x, y, 1.0); - glam::vec2(v.x, v.y) -} - -/// Returns a local position from another local -#[inline] -#[deprecated] -#[doc(hidden)] -#[allow(deprecated)] -pub fn mat3_local_to_local(x: f32, y: f32, from: glam::Mat3, to: glam::Mat3) -> glam::Vec2 { - let from_point = mat3_local_to_screen(x, y, from); - mat3_screen_to_local(from_point.x, from_point.y, to) -} diff --git a/crates/notan_oddio/Cargo.toml b/crates/notan_oddio/Cargo.toml index e104a3d3..9cf44856 100644 --- a/crates/notan_oddio/Cargo.toml +++ b/crates/notan_oddio/Cargo.toml @@ -1,17 +1,20 @@ [package] name = "notan_oddio" -version = "0.9.5" -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides support for Audio features using Oddio" [dependencies] -log = "0.4.17" -hashbrown = "0.13.2" -notan_audio = { path = "../notan_audio", version = "0.9.5" } -cpal = { version = "0.14.2", features = ["wasm-bindgen"] } +notan_audio.workspace = true + +log.workspace = true +hashbrown.workspace = true + +cpal = { version = "0.16", features = ["wasm-bindgen"] } oddio = "0.6.2" -symphonia = { version = "0.5.2", features = ["mp3"] } +symphonia = { version = "0.5.4", features = ["mp3"] } diff --git a/crates/notan_oddio/src/backend.rs b/crates/notan_oddio/src/backend.rs index 75baa053..d775ac20 100644 --- a/crates/notan_oddio/src/backend.rs +++ b/crates/notan_oddio/src/backend.rs @@ -22,15 +22,16 @@ enum AudioHandle { Cycle(CycleHandle), } +#[allow(mismatched_lifetime_syntaxes)] impl AudioHandle { - fn as_stop(&mut self) -> StopControl { + fn as_stop(&mut self) -> StopControl<'_> { match self { AudioHandle::Frame(h) => h.control::, _>(), AudioHandle::Cycle(h) => h.control::, _>(), } } - fn as_gain(&mut self) -> GainControl { + fn as_gain(&mut self) -> GainControl<'_> { match self { AudioHandle::Frame(h) => h.control::, _>(), AudioHandle::Cycle(h) => h.control::, _>(), @@ -239,8 +240,9 @@ impl InnerBackend { oddio::run(&mixer, sample_rate.0, frames); }, |err| { - log::error!("{:?}", err); + log::error!("{err:?}"); }, + None, ) .map_err(|e| format!("{e:?}"))?; @@ -306,21 +308,21 @@ impl InnerBackend { fn pause(&mut self, sound: u64) { match self.sounds.get_mut(&sound) { - None => log::warn!("Cannot pause sound, invalid id: {}", sound), + None => log::warn!("Cannot pause sound, invalid id: {sound}"), Some(s) => s.handle.as_stop().pause(), } } fn resume(&mut self, sound: u64) { match self.sounds.get_mut(&sound) { - None => log::warn!("Cannot resume sound, invalid id: {}", sound), + None => log::warn!("Cannot resume sound, invalid id: {sound}"), Some(s) => s.handle.as_stop().resume(), } } fn stop(&mut self, sound: u64) { match self.sounds.get_mut(&sound) { - None => log::warn!("Cannot stop sound, invalid id: {}", sound), + None => log::warn!("Cannot stop sound, invalid id: {sound}"), Some(s) => s.handle.as_stop().stop(), } } @@ -343,7 +345,7 @@ impl InnerBackend { fn set_volume(&mut self, sound: u64, volume: f32) { match self.sounds.get_mut(&sound) { - None => log::warn!("Cannot set volume for sound: {}", sound), + None => log::warn!("Cannot set volume for sound: {sound}"), Some(s) => { s.volume = volume; s.handle.as_gain().set_gain(volume_as_gain(volume)); @@ -367,11 +369,7 @@ impl InnerBackend { self.sounds.remove(id); }); - log::debug!( - "Audio resources cleaned: Sources({:?}) - Sounds({:?})", - sources, - sounds, - ); + log::trace!("Audio resources cleaned: Sources({sources:?}) - Sounds({sounds:?})",); } } @@ -379,7 +377,7 @@ impl InnerBackend { // with headphones I can hear -90, so I opted to to -100 fn volume_as_gain(volume: f32) -> f32 { let v = 1.0 - volume; - v * 100.0 * -1.0 + -(v * 100.0) } #[cfg(test)] diff --git a/crates/notan_oddio/src/decoder.rs b/crates/notan_oddio/src/decoder.rs index d9ab8573..90079728 100644 --- a/crates/notan_oddio/src/decoder.rs +++ b/crates/notan_oddio/src/decoder.rs @@ -43,7 +43,13 @@ fn decode_bytes(bytes: Vec) -> Result<(Vec, u32), String> { .codec_params() .sample_rate .ok_or_else(|| "Cannot get sample rate".to_string())?; - let samples = get_samples(&mut decoder, &mut format, track_id)?; + + let is_stereo = decoder + .codec_params() + .channels + .is_some_and(|ch| ch.count() == 2); + + let samples = get_samples(&mut decoder, &mut format, track_id, is_stereo)?; Ok((samples, sample_rate)) } @@ -51,6 +57,7 @@ fn get_samples( decoder: &mut Box, format: &mut Box, track_id: u32, + is_stereo: bool, ) -> Result, String> { let mut samples = vec![]; loop { @@ -76,9 +83,33 @@ fn get_samples( }; } + // duplicate mono samples to make it stereo? + if !is_stereo { + mono_to_stereo(&mut samples); + } + Ok(samples) } +fn mono_to_stereo(samples: &mut Vec) { + let original_len = samples.len(); + samples.resize(original_len * 2, 0.0); + + let mut write_idx = samples.len() - 1; + let mut read_idx = original_len - 1; + + while read_idx > 0 { + samples[write_idx] = samples[read_idx]; + samples[write_idx - 1] = samples[read_idx]; + + write_idx -= 2; + read_idx -= 1; + } + + samples[write_idx] = samples[read_idx]; + samples[write_idx - 1] = samples[read_idx]; +} + fn decode_packet( samples: &mut Vec, decoder: &mut Box, diff --git a/crates/notan_oddio/src/webaudio.rs b/crates/notan_oddio/src/webaudio.rs index f7c82885..b5c3111b 100644 --- a/crates/notan_oddio/src/webaudio.rs +++ b/crates/notan_oddio/src/webaudio.rs @@ -1,5 +1,3 @@ -#![cfg(target_arch = "wasm32")] - use hashbrown::HashMap; use notan_audio::AudioBackend; use oddio::Frames; diff --git a/crates/notan_random/Cargo.toml b/crates/notan_random/Cargo.toml index 3710bc2d..aba572f2 100644 --- a/crates/notan_random/Cargo.toml +++ b/crates/notan_random/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "notan_random" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a simple set of RNG utils for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8.5" -rand_pcg = "0.3.1" +rand = "0.9" +rand_pcg = "0.9" [target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2.8", features = ["js"] } +getrandom = { version = "0.3.4", features = ["wasm_js"] } diff --git a/crates/notan_random/src/utils.rs b/crates/notan_random/src/utils.rs index 72530e67..4d2fee62 100644 --- a/crates/notan_random/src/utils.rs +++ b/crates/notan_random/src/utils.rs @@ -4,6 +4,7 @@ use rand_pcg::Pcg32; use std::ops::{Deref, DerefMut}; /// Wrapper around a random generator based on Pcg32. +#[derive(Clone)] pub struct Random { rng: Pcg32, } @@ -36,7 +37,7 @@ impl DerefMut for Random { impl Default for Random { fn default() -> Self { Self { - rng: Pcg32::from_entropy(), + rng: Pcg32::from_os_rng(), } } } diff --git a/crates/notan_text/Cargo.toml b/crates/notan_text/Cargo.toml index 0ab31b09..dd8a7a25 100644 --- a/crates/notan_text/Cargo.toml +++ b/crates/notan_text/Cargo.toml @@ -1,21 +1,28 @@ [package] name = "notan_text" -version = "0.9.5" -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a simple Text API for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -notan_app = { path = "../notan_app", version = "0.9.5" } -notan_graphics = { path = "../notan_graphics", version = "0.9.5" } -notan_glyph = { path = "../notan_glyph", version = "0.9.5" } -notan_math = { path = "../notan_math", version = "0.9.5" } -log = "0.4.17" -lazy_static = "1.4.0" -parking_lot = "0.12.1" -hashbrown = "0.13.2" +notan_app.workspace = true +notan_graphics.workspace = true +notan_glyph.workspace = true +notan_math.workspace = true + +log.workspace = true +parking_lot.workspace = true +hashbrown.workspace = true + +lazy_static = "1.5.0" + +[features] +glsl-to-spirv = ["notan_glyph/glsl-to-spirv"] +shaderc = ["notan_glyph/shaderc"] diff --git a/crates/notan_text/src/config.rs b/crates/notan_text/src/config.rs index 09f3ff2b..f65f1387 100644 --- a/crates/notan_text/src/config.rs +++ b/crates/notan_text/src/config.rs @@ -24,6 +24,6 @@ fn parse_font(id: &str, data: Vec, gfx: &mut Graphics) -> Result() .ok_or("TextExtension is not added to Graphics")? .create_font(&data)?; - log::debug!("Asset '{}' parsed as TextExtension Font", id); + log::debug!("Asset '{id}' parsed as TextExtension Font"); Ok(font) } diff --git a/crates/notan_text/src/lib.rs b/crates/notan_text/src/lib.rs index 9ceeac61..df3387aa 100644 --- a/crates/notan_text/src/lib.rs +++ b/crates/notan_text/src/lib.rs @@ -1,6 +1,7 @@ mod calculator; mod config; +use hashbrown::HashMap; use lazy_static::lazy_static; use notan_app::{ExtContainer, GfxExtension, GfxRenderer, Graphics}; use notan_glyph::ab_glyph::FontArc; @@ -12,7 +13,6 @@ use notan_graphics::color::Color; use notan_graphics::pipeline::ClearOptions; use notan_graphics::{Device, RenderTexture, Renderer, Texture}; use std::any::TypeId; -use std::collections::HashMap; use std::ops::DerefMut; pub use calculator::Calculator; @@ -299,8 +299,8 @@ impl Drop for ChainTextBuilder<'_, '_> { // get minX, minY, maxX, maxY for sections here... pub struct Text<'a> { - pub(crate) width: i32, - pub(crate) height: i32, + pub(crate) width: u32, + pub(crate) height: u32, pub(crate) sections: Vec>, pub(crate) pipeline_type: Option, pub(crate) clear_options: Option, @@ -310,7 +310,7 @@ pub struct Text<'a> { } impl<'a> Text<'a> { - pub fn new(width: i32, height: i32) -> Self { + pub fn new(width: u32, height: u32) -> Self { Self { sections: vec![], width, diff --git a/crates/notan_utils/Cargo.toml b/crates/notan_utils/Cargo.toml index a1ed113c..8e6e7a0d 100644 --- a/crates/notan_utils/Cargo.toml +++ b/crates/notan_utils/Cargo.toml @@ -1,25 +1,26 @@ [package] name = "notan_utils" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a simple set of utils Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -instant = { version = "0.1.12", features = ["wasm-bindgen"] } -log = "0.4.17" +web-time = "1.1.0" +log.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "0.2.83", optional = true } -js-sys = { version = "0.3.60", optional = true } -web-sys = { version = "0.3.60", optional = true } -mime_guess = { version = "2.0.4", optional = true } +wasm-bindgen = { workspace = true, optional = true } +js-sys = { workspace = true, optional = true } +web-sys = { workspace = true, optional = true } + +mime_guess = { version = "2.0.5", optional = true } [features] save_file = ["mime_guess", "wasm-bindgen", "js-sys", "web-sys", "web-sys?/Window", "web-sys?/Blob", "web-sys?/BlobPropertyBag", "web-sys?/Url", "web-sys?/Element", "web-sys?/HtmlAnchorElement"] diff --git a/crates/notan_utils/src/lib.rs b/crates/notan_utils/src/lib.rs index 9156c43f..66c75a53 100644 --- a/crates/notan_utils/src/lib.rs +++ b/crates/notan_utils/src/lib.rs @@ -4,4 +4,4 @@ mod save_file; #[cfg(feature = "save_file")] pub use save_file::*; -pub use instant::{Duration, Instant}; +pub use web_time::{Duration, Instant}; diff --git a/crates/notan_utils/src/save_file.rs b/crates/notan_utils/src/save_file.rs index dd707799..fdd7a0b3 100644 --- a/crates/notan_utils/src/save_file.rs +++ b/crates/notan_utils/src/save_file.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "save_file")] - use std::path::Path; #[cfg(not(target_arch = "wasm32"))] @@ -28,11 +26,11 @@ pub fn save_file>(path: P, bytes: &[u8]) -> Result<(), String> { let array = Array::new(); array.push(&u8_buff.buffer()); - let blob = web_sys::Blob::new_with_u8_array_sequence_and_options( - &array, - web_sys::BlobPropertyBag::new().type_(&mime), - ) - .map_err(|_| "Cannot create a blob from file".to_string())?; + let blob_property = web_sys::BlobPropertyBag::new(); + blob_property.set_type(&mime); + + let blob = web_sys::Blob::new_with_u8_array_sequence_and_options(&array, &blob_property) + .map_err(|_| "Cannot create a blob from file".to_string())?; let url = web_sys::Url::create_object_url_with_blob(&blob) .map_err(|_| "Cannot create a blob from file".to_string())?; diff --git a/crates/notan_web/Cargo.toml b/crates/notan_web/Cargo.toml index 8d7820de..519e5a30 100644 --- a/crates/notan_web/Cargo.toml +++ b/crates/notan_web/Cargo.toml @@ -1,32 +1,33 @@ [package] name = "notan_web" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a web/wasm32 backend for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -wasm-bindgen = "0.2.83" -js-sys = "0.3.60" -wasm-bindgen-futures = "0.4.33" +notan_core.workspace = true +notan_app.workspace = true +notan_glow.workspace = true +notan_graphics.workspace = true +notan_audio = { workspace = true, optional = true } +notan_oddio = { workspace = true, optional = true } + +log.workspace = true +wasm-bindgen.workspace = true +js-sys.workspace = true +wasm-bindgen-futures.workspace = true + console_error_panic_hook = "0.1.7" -futures-util = "0.3.25" -notan_core = { path = "../notan_core", version = "0.9.5" } -notan_app = { path = "../notan_app", version = "0.9.5" } -notan_glow = { path = "../notan_glow", version = "0.9.5" } -notan_graphics = { path = "../notan_graphics", version = "0.9.5" } -notan_audio = { path = "../notan_audio", version = "0.9.5", optional = true } -notan_oddio = { path = "../notan_oddio", version = "0.9.5", optional = true } [dependencies.web-sys] -version = "0.3.60" +workspace = true features = [ "Screen", "Document", @@ -57,3 +58,6 @@ features = [ audio = ["notan_app/audio", "notan_audio", "notan_oddio"] drop_files = ["web-sys/DragEvent", "web-sys/DataTransfer", "web-sys/FileList", "web-sys/File", "web-sys/DataTransferItemList", "web-sys/DataTransferItem"] clipboard = ["web-sys/Navigator", "web-sys/DataTransfer"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(web_sys_unstable_apis)'] } \ No newline at end of file diff --git a/crates/notan_web/src/audio.rs b/crates/notan_web/src/audio.rs index 944af026..36a52b75 100644 --- a/crates/notan_web/src/audio.rs +++ b/crates/notan_web/src/audio.rs @@ -1,4 +1,3 @@ -#![cfg(feature = "audio")] use crate::utils::window_remove_event_listener; use js_sys::eval; diff --git a/crates/notan_web/src/backend.rs b/crates/notan_web/src/backend.rs index 5e6d025b..7ec61583 100644 --- a/crates/notan_web/src/backend.rs +++ b/crates/notan_web/src/backend.rs @@ -2,8 +2,10 @@ use crate::audio::enable_webaudio; use crate::utils::request_animation_frame; use crate::window::WebWindowBackend; -use notan_app::{App, Backend, BackendSystem, EventIterator, InitializeFn, WindowBackend}; -use notan_app::{FrameState, WindowConfig}; +use notan_app::WindowConfig; +use notan_app::{ + App, AppLoader, Backend, BackendRunner, BackendSystem, EventIterator, WindowBackend, +}; #[cfg(feature = "audio")] use notan_audio::AudioBackend; use notan_graphics::DeviceBackend; @@ -72,44 +74,48 @@ impl Backend for WebBackend { } } -impl BackendSystem for WebBackend { - fn initialize(&mut self, window: WindowConfig) -> Result>, String> - where - S: 'static, - R: FnMut(&mut App, &mut S) -> Result + 'static, - { +struct WebRunner; + +impl BackendRunner for WebRunner { + fn run( + &mut self, + mut app_loader: Box, + window_config: WindowConfig, + ) -> Result<(), String> { let callback = Rc::new(RefCell::new(None)); - let win = WebWindowBackend::new(window, self.events.clone(), callback.clone())?; - self.window = Some(win); - - Ok(Box::new(move |mut app: App, mut state: S, mut cb: R| { - let inner_callback = callback.clone(); - - *callback.borrow_mut() = Some(Closure::wrap(Box::new(move || { - let backend = backend(&mut app); - if !backend.exit_requested { - let win = backend.window.as_mut().unwrap(); - win.check_dpi(); - - if win.lazy_loop() { - *win.frame_requested.borrow_mut() = false; - } else { - request_animation_frame( - &win.window, - inner_callback.borrow().as_ref().unwrap(), - ); - } + let backend: &mut WebBackend = app_loader.backend().downcast_mut().unwrap(); + let win = WebWindowBackend::new(window_config, backend.events.clone(), callback.clone())?; + backend.window = Some(win); + let mut app_runner = app_loader.load()?; + let inner_callback = callback.clone(); + + *callback.borrow_mut() = Some(Closure::wrap(Box::new(move || { + let backend = get_backend_mut(app_runner.app_mut()); + if !backend.exit_requested { + let win = backend.window.as_mut().unwrap(); + win.check_dpi(); + + if win.lazy_loop() { + *win.frame_requested.borrow_mut() = false; + } else { + request_animation_frame(&win.window, inner_callback.borrow().as_ref().unwrap()); } + } - if let Err(e) = cb(&mut app, &mut state) { - log::error!("{}", e); - } - }) as Box)); + if let Err(e) = app_runner.run() { + log::error!("{}", e); + } + }) as Box)); - let window = web_sys::window().unwrap(); - request_animation_frame(&window, callback.borrow().as_ref().unwrap()); - Ok(()) - })) + let window = web_sys::window().unwrap(); + request_animation_frame(&window, callback.borrow().as_ref().unwrap()); + Ok(()) + } +} + +impl BackendSystem for WebBackend { + fn runner(&self) -> Box { + Box::new(WebRunner) } fn get_graphics_backend(&self) -> Box { @@ -131,6 +137,6 @@ impl BackendSystem for WebBackend { } } -fn backend(app: &mut App) -> &mut WebBackend { +fn get_backend_mut(app: &mut App) -> &mut WebBackend { app.backend.downcast_mut::().unwrap() } diff --git a/crates/notan_web/src/clipboard.rs b/crates/notan_web/src/clipboard.rs index c7cbbf54..e0c440cb 100644 --- a/crates/notan_web/src/clipboard.rs +++ b/crates/notan_web/src/clipboard.rs @@ -52,15 +52,14 @@ pub fn enable_clipboard(win: &mut WebWindowBackend) -> Result<(), String> { pub fn set_clipboard_text(text: &str) { if let Some(window) = web_sys::window() { - if let Some(clipboard) = window.navigator().clipboard() { - let promise = clipboard.write_text(text); - let future = wasm_bindgen_futures::JsFuture::from(promise); - let future = async move { - if let Err(err) = future.await { - log::error!("failed to set text on clipboard: {:?}", err); - } - }; - wasm_bindgen_futures::spawn_local(future); - } + let clipboard = window.navigator().clipboard(); + let promise = clipboard.write_text(text); + let future = wasm_bindgen_futures::JsFuture::from(promise); + let future = async move { + if let Err(err) = future.await { + log::error!("failed to set text on clipboard: {:?}", err); + } + }; + wasm_bindgen_futures::spawn_local(future); } } diff --git a/crates/notan_web/src/keyboard.rs b/crates/notan_web/src/keyboard.rs index 95525a61..1960a06b 100644 --- a/crates/notan_web/src/keyboard.rs +++ b/crates/notan_web/src/keyboard.rs @@ -55,52 +55,53 @@ pub fn enable_keyboard( // Code from winit pub fn keyboard_code(code: &str) -> Option { Some(match code { - "Digit1" => KeyCode::Key1, - "Digit2" => KeyCode::Key2, - "Digit3" => KeyCode::Key3, - "Digit4" => KeyCode::Key4, - "Digit5" => KeyCode::Key5, - "Digit6" => KeyCode::Key6, - "Digit7" => KeyCode::Key7, - "Digit8" => KeyCode::Key8, - "Digit9" => KeyCode::Key9, - "Digit0" => KeyCode::Key0, - "KeyA" => KeyCode::A, - "KeyB" => KeyCode::B, - "KeyC" => KeyCode::C, - "KeyD" => KeyCode::D, - "KeyE" => KeyCode::E, - "KeyF" => KeyCode::F, - "KeyG" => KeyCode::G, - "KeyH" => KeyCode::H, - "KeyI" => KeyCode::I, - "KeyJ" => KeyCode::J, - "KeyK" => KeyCode::K, - "KeyL" => KeyCode::L, - "KeyM" => KeyCode::M, - "KeyN" => KeyCode::N, - "KeyO" => KeyCode::O, - "KeyP" => KeyCode::P, - "KeyQ" => KeyCode::Q, - "KeyR" => KeyCode::R, - "KeyS" => KeyCode::S, - "KeyT" => KeyCode::T, - "KeyU" => KeyCode::U, - "KeyV" => KeyCode::V, - "KeyW" => KeyCode::W, - "KeyX" => KeyCode::X, - "KeyY" => KeyCode::Y, - "KeyZ" => KeyCode::Z, + "Again" => KeyCode::Again, + "AltLeft" => KeyCode::AltLeft, + "AltRight" => KeyCode::AltRight, + "ArrowDown" => KeyCode::ArrowDown, + "ArrowLeft" => KeyCode::ArrowLeft, + "ArrowRight" => KeyCode::ArrowRight, + "ArrowUp" => KeyCode::ArrowUp, + "AudioVolumeDown" => KeyCode::AudioVolumeDown, + "AudioVolumeMute" => KeyCode::AudioVolumeMute, + "AudioVolumeUp" => KeyCode::AudioVolumeUp, + "Backquote" => KeyCode::Backquote, + "Backslash" => KeyCode::Backslash, + "Backspace" => KeyCode::Backspace, + "BracketLeft" => KeyCode::BracketLeft, + "BracketRight" => KeyCode::BracketRight, + "BrowserBack" => KeyCode::BrowserBack, + "BrowserFavorites" => KeyCode::BrowserFavorites, + "BrowserForward" => KeyCode::BrowserForward, + "BrowserHome" => KeyCode::BrowserHome, + "BrowserRefresh" => KeyCode::BrowserRefresh, + "BrowserSearch" => KeyCode::BrowserSearch, + "BrowserStop" => KeyCode::BrowserStop, + "CapsLock" => KeyCode::CapsLock, + "Comma" => KeyCode::Comma, + "ContextMenu" => KeyCode::ContextMenu, + "ControlLeft" => KeyCode::ControlLeft, + "ControlRight" => KeyCode::ControlRight, + "Convert" => KeyCode::Convert, + "Copy" => KeyCode::Copy, + "Cut" => KeyCode::Cut, + "Delete" => KeyCode::Delete, + "Digit0" => KeyCode::Digit0, + "Digit1" => KeyCode::Digit1, + "Digit2" => KeyCode::Digit2, + "Digit3" => KeyCode::Digit3, + "Digit4" => KeyCode::Digit4, + "Digit5" => KeyCode::Digit5, + "Digit6" => KeyCode::Digit6, + "Digit7" => KeyCode::Digit7, + "Digit8" => KeyCode::Digit8, + "Digit9" => KeyCode::Digit9, + "Eject" => KeyCode::Eject, + "End" => KeyCode::End, + "Enter" => KeyCode::Enter, + "Equal" => KeyCode::Equal, "Escape" => KeyCode::Escape, "F1" => KeyCode::F1, - "F2" => KeyCode::F2, - "F3" => KeyCode::F3, - "F4" => KeyCode::F4, - "F5" => KeyCode::F5, - "F6" => KeyCode::F6, - "F7" => KeyCode::F7, - "F8" => KeyCode::F8, - "F9" => KeyCode::F9, "F10" => KeyCode::F10, "F11" => KeyCode::F11, "F12" => KeyCode::F12, @@ -111,30 +112,72 @@ pub fn keyboard_code(code: &str) -> Option { "F17" => KeyCode::F17, "F18" => KeyCode::F18, "F19" => KeyCode::F19, + "F2" => KeyCode::F2, "F20" => KeyCode::F20, "F21" => KeyCode::F21, "F22" => KeyCode::F22, "F23" => KeyCode::F23, "F24" => KeyCode::F24, - "PrintScreen" => KeyCode::Snapshot, - "ScrollLock" => KeyCode::Scroll, - "Pause" => KeyCode::Pause, - "Insert" => KeyCode::Insert, + "F3" => KeyCode::F3, + "F4" => KeyCode::F4, + "F5" => KeyCode::F5, + "F6" => KeyCode::F6, + "F7" => KeyCode::F7, + "F8" => KeyCode::F8, + "F9" => KeyCode::F9, + "Find" => KeyCode::Find, + "Fn" => KeyCode::Fn, + "Help" => KeyCode::Help, "Home" => KeyCode::Home, - "Delete" => KeyCode::Delete, - "End" => KeyCode::End, - "PageDown" => KeyCode::PageDown, - "PageUp" => KeyCode::PageUp, - "ArrowLeft" => KeyCode::Left, - "ArrowUp" => KeyCode::Up, - "ArrowRight" => KeyCode::Right, - "ArrowDown" => KeyCode::Down, - "Backspace" => KeyCode::Back, - "Enter" => KeyCode::Return, - "Space" => KeyCode::Space, - "Compose" => KeyCode::Compose, - "Caret" => KeyCode::Caret, - "NumLock" => KeyCode::Numlock, + "Insert" => KeyCode::Insert, + "IntlBackslash" => KeyCode::IntlBackslash, + "IntlRo" => KeyCode::IntlRo, + "IntlYen" => KeyCode::IntlYen, + "KanaMode" => KeyCode::KanaMode, + "KeyA" => KeyCode::KeyA, + "KeyB" => KeyCode::KeyB, + "KeyC" => KeyCode::KeyC, + "KeyD" => KeyCode::KeyD, + "KeyE" => KeyCode::KeyE, + "KeyF" => KeyCode::KeyF, + "KeyG" => KeyCode::KeyG, + "KeyH" => KeyCode::KeyH, + "KeyI" => KeyCode::KeyI, + "KeyJ" => KeyCode::KeyJ, + "KeyK" => KeyCode::KeyK, + "KeyL" => KeyCode::KeyL, + "KeyM" => KeyCode::KeyM, + "KeyN" => KeyCode::KeyN, + "KeyO" => KeyCode::KeyO, + "KeyP" => KeyCode::KeyP, + "KeyQ" => KeyCode::KeyQ, + "KeyR" => KeyCode::KeyR, + "KeyS" => KeyCode::KeyS, + "KeyT" => KeyCode::KeyT, + "KeyU" => KeyCode::KeyU, + "KeyV" => KeyCode::KeyV, + "KeyW" => KeyCode::KeyW, + "KeyX" => KeyCode::KeyX, + "KeyY" => KeyCode::KeyY, + "KeyZ" => KeyCode::KeyZ, + "Lang1" => KeyCode::Lang1, + "Lang2" => KeyCode::Lang2, + "Lang3" => KeyCode::Lang3, + "Lang4" => KeyCode::Lang4, + "Lang5" => KeyCode::Lang5, + "LaunchApp1" => KeyCode::LaunchApp1, + "LaunchApp2" => KeyCode::LaunchApp2, + "LaunchMail" => KeyCode::LaunchMail, + "MediaPlayPause" => KeyCode::MediaPlayPause, + "MediaSelect" => KeyCode::MediaSelect, + "MediaStop" => KeyCode::MediaStop, + "MediaTrackNext" => KeyCode::MediaTrackNext, + "MediaTrackPrevious" => KeyCode::MediaTrackPrevious, + "MetaLeft" => KeyCode::Meta, + "MetaRight" => KeyCode::Meta, + "Minus" => KeyCode::Minus, + "NonConvert" => KeyCode::NonConvert, + "NumLock" => KeyCode::NumLock, "Numpad0" => KeyCode::Numpad0, "Numpad1" => KeyCode::Numpad1, "Numpad2" => KeyCode::Numpad2, @@ -145,73 +188,40 @@ pub fn keyboard_code(code: &str) -> Option { "Numpad7" => KeyCode::Numpad7, "Numpad8" => KeyCode::Numpad8, "Numpad9" => KeyCode::Numpad9, - "AbntC1" => KeyCode::AbntC1, - "AbntC2" => KeyCode::AbntC2, - "NumpadAdd" => KeyCode::Add, - "Quote" => KeyCode::Apostrophe, - "Apps" => KeyCode::Apps, - "At" => KeyCode::At, - "Ax" => KeyCode::Ax, - "Backslash" => KeyCode::Backslash, - "Calculator" => KeyCode::Calculator, - "Capital" => KeyCode::Capital, - "Semicolon" => KeyCode::Semicolon, - "Comma" => KeyCode::Comma, - "Convert" => KeyCode::Convert, - "NumpadDecimal" => KeyCode::Decimal, - "NumpadDivide" => KeyCode::Divide, - "Equal" => KeyCode::Equals, - "Backquote" => KeyCode::Grave, - "Kana" => KeyCode::Kana, - "Kanji" => KeyCode::Kanji, - "AltLeft" => KeyCode::LAlt, - "BracketLeft" => KeyCode::LBracket, - "ControlLeft" => KeyCode::LControl, - "ShiftLeft" => KeyCode::LShift, - "MetaLeft" => KeyCode::LWin, - "Mail" => KeyCode::Mail, - "MediaSelect" => KeyCode::MediaSelect, - "MediaStop" => KeyCode::MediaStop, - "Minus" => KeyCode::Minus, - "NumpadMultiply" => KeyCode::Multiply, - "Mute" => KeyCode::Mute, - "LaunchMyComputer" => KeyCode::MyComputer, - "NavigateForward" => KeyCode::NavigateForward, - "NavigateBackward" => KeyCode::NavigateBackward, - "NextTrack" => KeyCode::NextTrack, - "NoConvert" => KeyCode::NoConvert, + "NumpadAdd" => KeyCode::NumpadAdd, "NumpadComma" => KeyCode::NumpadComma, + "NumpadDecimal" => KeyCode::NumpadDecimal, + "NumpadDivide" => KeyCode::NumpadDivide, "NumpadEnter" => KeyCode::NumpadEnter, - "NumpadEquals" => KeyCode::NumpadEquals, - "OEM102" => KeyCode::OEM102, + "NumpadEqual" => KeyCode::NumpadEqual, + "NumpadMultiply" => KeyCode::NumpadMultiply, + "NumpadParenLeft" => KeyCode::NumpadParenLeft, + "NumpadParenRight" => KeyCode::NumpadParenRight, + "NumpadSubtract" => KeyCode::NumpadSubtract, + "Open" => KeyCode::Open, + "PageDown" => KeyCode::PageDown, + "PageUp" => KeyCode::PageUp, + "Paste" => KeyCode::Paste, + "Pause" => KeyCode::Pause, "Period" => KeyCode::Period, - "PlayPause" => KeyCode::PlayPause, "Power" => KeyCode::Power, - "PrevTrack" => KeyCode::PrevTrack, - "AltRight" => KeyCode::RAlt, - "BracketRight" => KeyCode::RBracket, - "ControlRight" => KeyCode::RControl, - "ShiftRight" => KeyCode::RShift, - "MetaRight" => KeyCode::RWin, + "PrintScreen" => KeyCode::PrintScreen, + "Props" => KeyCode::Props, + "Quote" => KeyCode::Quote, + "ScrollLock" => KeyCode::ScrollLock, + "Select" => KeyCode::Select, + "Semicolon" => KeyCode::Semicolon, + "ShiftLeft" => KeyCode::ShiftLeft, + "ShiftRight" => KeyCode::ShiftRight, "Slash" => KeyCode::Slash, "Sleep" => KeyCode::Sleep, - "Stop" => KeyCode::Stop, - "NumpadSubtract" => KeyCode::Subtract, - "Sysrq" => KeyCode::Sysrq, + "Space" => KeyCode::Space, "Tab" => KeyCode::Tab, - "Underline" => KeyCode::Underline, - "Unlabeled" => KeyCode::Unlabeled, - "AudioVolumeDown" => KeyCode::VolumeDown, - "AudioVolumeUp" => KeyCode::VolumeUp, - "Wake" => KeyCode::Wake, - "WebBack" => KeyCode::WebBack, - "WebFavorites" => KeyCode::WebFavorites, - "WebForward" => KeyCode::WebForward, - "WebHome" => KeyCode::WebHome, - "WebRefresh" => KeyCode::WebRefresh, - "WebSearch" => KeyCode::WebSearch, - "WebStop" => KeyCode::WebStop, - "Yen" => KeyCode::Yen, + "Undo" => KeyCode::Undo, + "VolumeDown" => KeyCode::AudioVolumeDown, + "VolumeMute" => KeyCode::AudioVolumeMute, + "VolumeUp" => KeyCode::AudioVolumeUp, + "WakeUp" => KeyCode::WakeUp, _ => return None, }) } diff --git a/crates/notan_web/src/lib.rs b/crates/notan_web/src/lib.rs index f3957723..1e183eb5 100644 --- a/crates/notan_web/src/lib.rs +++ b/crates/notan_web/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg(target_arch = "wasm32")] + mod backend; mod clipboard; mod keyboard; @@ -18,4 +20,3 @@ compile_error!("feature \"clipboard\" requires web_sys_unstable_apis to be enabl pub mod prelude; pub use backend::*; -pub use notan_glow::texture_source::*; diff --git a/crates/notan_web/src/mouse.rs b/crates/notan_web/src/mouse.rs index 0da94957..9134441b 100644 --- a/crates/notan_web/src/mouse.rs +++ b/crates/notan_web/src/mouse.rs @@ -26,7 +26,7 @@ fn mouse_button_to_nae(btn: i16) -> MouseButton { 0 => MouseButton::Left, 1 => MouseButton::Middle, 2 => MouseButton::Right, - n => MouseButton::Other(n as u8), + n => MouseButton::Other(n as u16), } } @@ -88,6 +88,7 @@ pub fn enable_mouse( &mut last_x.borrow_mut(), &mut last_y.borrow_mut(), ); + let _ = canvas.focus(); add_evt_down(Event::MouseDown { button, x, y }); }, )?); diff --git a/crates/notan_web/src/utils.rs b/crates/notan_web/src/utils.rs index a853f41a..126e1198 100644 --- a/crates/notan_web/src/utils.rs +++ b/crates/notan_web/src/utils.rs @@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use web_sys::{Document, HtmlCanvasElement, Window}; -pub fn set_size_dpi(canvas: &HtmlCanvasElement, width: i32, height: i32) { +pub fn set_size_dpi(canvas: &HtmlCanvasElement, width: u32, height: u32) { let auto_res = canvas .get_attribute("notan-auto-res") .unwrap_or_else(|| "false".to_string()) @@ -69,6 +69,14 @@ pub fn get_or_create_canvas(doc: &Document, canvas_id: &str) -> Result (i32, i32) { +pub fn get_notan_size(canvas: &HtmlCanvasElement) -> (u32, u32) { let width = canvas .get_attribute("notan-width") .unwrap_or_else(|| "0".to_string()) - .parse::() + .parse::() .unwrap_or(0); let height = canvas .get_attribute("notan-height") .unwrap_or_else(|| "0".to_string()) - .parse::() + .parse::() .unwrap_or(0); (width, height) diff --git a/crates/notan_web/src/window.rs b/crates/notan_web/src/window.rs index 1e373a6c..7447ff88 100644 --- a/crates/notan_web/src/window.rs +++ b/crates/notan_web/src/window.rs @@ -36,11 +36,11 @@ pub struct WebWindowBackend { events: Rc>, fullscreen_requested: Rc>>, - fullscreen_last_size: Rc>>, + fullscreen_last_size: Rc>>, fullscreen_callback_ref: Option>, - min_size: Option<(i32, i32)>, - max_size: Option<(i32, i32)>, + min_size: Option<(u32, u32)>, + max_size: Option<(u32, u32)>, resize_callback_ref: Option>, _context_menu_callback_ref: Closure, @@ -83,7 +83,7 @@ impl WebWindowBackend { .document() .ok_or("Can't access document dom object ")?; - let canvas = get_or_create_canvas(&document, &config.canvas_id)?; + let canvas = get_or_create_canvas(&document, &config.app_id)?; let visible = config.visible; canvas_visible(&canvas, visible); @@ -98,6 +98,8 @@ impl WebWindowBackend { e.prevent_default(); })?; + let _ = canvas.focus(); + let fullscreen_requested = Rc::new(RefCell::new(None)); let fullscreen_last_size = Rc::new(RefCell::new(None)); let fullscreen_callback_ref = None; @@ -193,8 +195,8 @@ impl WebWindowBackend { let (ww, hh) = if self.config.maximized { ( - self.canvas_parent.client_width(), - self.canvas_parent.client_height(), + self.canvas_parent.client_width() as _, + self.canvas_parent.client_height() as _, ) } else { (self.config.width, self.config.height) @@ -262,6 +264,12 @@ impl WindowBackend for WebWindowBackend { *self.captured.borrow() } + fn container_size(&self) -> (i32, i32) { + let width = self.canvas_parent.client_width(); + let height = self.canvas_parent.client_height(); + (width, height) + } + fn cursor(&self) -> CursorIcon { self.cursor } @@ -291,6 +299,17 @@ impl WindowBackend for WebWindowBackend { *self.lazy.borrow() } + fn is_focused(&self) -> bool { + let has_focus = self.document.has_focus().ok().unwrap_or_default(); + if !has_focus { + return false; + } + + self.document + .active_element() + .is_some_and(|el| el.id() == self.canvas.id()) + } + // No operation, as unsupported in browser fn mouse_passthrough(&mut self) -> bool { false @@ -317,12 +336,6 @@ impl WindowBackend for WebWindowBackend { (width, height) } - fn container_size(&self) -> (i32, i32) { - let width = self.canvas_parent.client_width(); - let height = self.canvas_parent.client_height(); - (width, height) - } - // No operation, as unsupported in browser fn set_always_on_top(&mut self, _enabled: bool) {} @@ -348,6 +361,9 @@ impl WindowBackend for WebWindowBackend { } } + // No operation, as unsupported in browser + fn set_cursor_position(&mut self, _x: f32, _y: f32) {} + fn set_fullscreen(&mut self, enabled: bool) { *self.fullscreen_requested.borrow_mut() = Some(enabled); } @@ -369,7 +385,7 @@ impl WindowBackend for WebWindowBackend { // No operation, as unsupported in browser fn set_position(&mut self, _x: i32, _y: i32) {} - fn set_size(&mut self, width: i32, height: i32) { + fn set_size(&mut self, width: u32, height: u32) { set_size_dpi(&self.canvas, width as _, height as _); self.config.width = width; self.config.height = height; @@ -382,7 +398,7 @@ impl WindowBackend for WebWindowBackend { } } - fn size(&self) -> (i32, i32) { + fn size(&self) -> (u32, u32) { get_notan_size(&self.canvas) } @@ -419,7 +435,7 @@ fn enable_fullscreen(win: &mut WebWindowBackend) -> Result<(), String> { win.fullscreen_callback_ref = Some(window_add_event_listener("fullscreenchange", move |_| { let (width, height) = if document.fullscreen() { - (canvas.client_width(), canvas.client_height()) + (canvas.client_width() as _, canvas.client_height() as _) } else { match *last_size.borrow() { Some(size) => size, @@ -449,8 +465,8 @@ fn fullscreen_dispatcher_callback(win: &mut WebWindowBackend) -> Rc Result<(), String> { let max_size = win.max_size; let add_event = win.add_event_fn(); win.resize_callback_ref = Some(window_add_event_listener("resize", move |_| { - let mut p_width = parent.client_width(); - let mut p_height = parent.client_height(); + let mut p_width = parent.client_width() as _; + let mut p_height = parent.client_height() as _; if let Some((w, h)) = min_size { if p_width < w { @@ -500,7 +516,7 @@ fn enable_resize(win: &mut WebWindowBackend) -> Result<(), String> { } } - set_size_dpi(&canvas, p_width as _, p_height as _); + set_size_dpi(&canvas, p_width, p_height); add_event(Event::WindowResize { width: p_width, height: p_height, diff --git a/crates/notan_winit/Cargo.toml b/crates/notan_winit/Cargo.toml index 0bf7fa96..a23711db 100644 --- a/crates/notan_winit/Cargo.toml +++ b/crates/notan_winit/Cargo.toml @@ -1,32 +1,34 @@ [package] name = "notan_winit" -version = "0.9.5" -authors = ["Nazarí González "] -edition = "2021" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true readme = "README.md" -homepage = "https://github.com/Nazariglez/notan" -repository = "https://github.com/Nazariglez/notan" -license = "MIT OR Apache-2.0" description = "Provides a native backend using winit for Notan" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.17" -notan_core = { path = "../notan_core", version = "0.9.5" } -notan_app = { path = "../notan_app", version = "0.9.5" } -notan_glow = { path = "../notan_glow", version = "0.9.5" } -notan_audio = { path = "../notan_audio", version = "0.9.5", optional = true } -notan_oddio = { path = "../notan_oddio", version = "0.9.5", optional = true } -notan_input = { path = "../notan_input", version = "0.9.5", optional = true } -glutin = "0.30.3" -glutin-winit = "0.2.1" -winit = "0.27.5" -raw-window-handle = "0.5.0" -arboard = { version = "3.2.0", optional = true, default-features = false } -webbrowser = { version = "0.8.4", optional = true } -mime_guess = { version = "2.0.4", optional = true } -image = { version = "0.24.5", default-features = false, features = ["jpeg", "png", "ico"] } +notan_core.workspace = true +notan_app.workspace = true +notan_glow.workspace = true +notan_audio = { workspace = true, optional = true } +notan_oddio = { workspace = true, optional = true } +notan_input = { workspace = true, optional = true } + +image.workspace = true +log.workspace = true + +glutin = "0.32.3" +glutin-winit = "0.5.0" +winit = "0.30.11" +raw-window-handle = "0.6.2" +arboard = { version = "3.4.1", optional = true, default-features = false } +webbrowser = { version = "1.0.6", optional = true } +mime_guess = { version = "2.0.5", optional = true } [features] audio = ["notan_app/audio", "notan_audio", "notan_oddio"] diff --git a/crates/notan_winit/src/backend.rs b/crates/notan_winit/src/backend.rs index 8fa65060..ae03951f 100644 --- a/crates/notan_winit/src/backend.rs +++ b/crates/notan_winit/src/backend.rs @@ -1,7 +1,8 @@ use crate::window::WinitWindowBackend; use crate::{keyboard, mouse, touch}; -use notan_app::{FrameState, WindowConfig}; -use winit::event_loop::ControlFlow; +use notan_app::{AppLoader, AppRunner, BackendRunner, FrameState, WindowConfig}; +use winit::application::ApplicationHandler; +use winit::event_loop::{ActiveEventLoop, ControlFlow}; #[cfg(feature = "clipboard")] use crate::clipboard; @@ -9,9 +10,7 @@ use crate::clipboard; #[cfg(feature = "drop_files")] use notan_app::DroppedFile; -use notan_app::{ - App, Backend, BackendSystem, DeviceBackend, Event, EventIterator, InitializeFn, WindowBackend, -}; +use notan_app::{Backend, BackendSystem, DeviceBackend, Event, EventIterator, WindowBackend}; #[cfg(feature = "audio")] use notan_audio::AudioBackend; #[cfg(feature = "audio")] @@ -24,7 +23,7 @@ use std::ffi::CString; #[cfg(feature = "audio")] use std::rc::Rc; -use winit::event::{Event as WEvent, WindowEvent}; +use winit::event::WindowEvent; use winit::event_loop::EventLoop; pub struct WinitBackend { @@ -54,10 +53,7 @@ impl Backend for WinitBackend { #[cfg(not(feature = "clipboard"))] { - log::warn!( - "Cannot set {} to clipboard without the feature 'clipboard' enabled.", - text - ); + log::warn!("Cannot set {text} to clipboard without the feature 'clipboard' enabled."); } } @@ -81,216 +77,320 @@ impl Backend for WinitBackend { #[cfg(feature = "links")] { if let Err(err) = webbrowser::open(url) { - log::error!("Error opening {}: {}", url, err); + log::error!("Error opening {url}: {err}"); } } #[cfg(not(feature = "links"))] { - log::warn!("Cannot {} link without the feature 'links' enabled.", url); + log::warn!("Cannot {url} link without the feature 'links' enabled."); } } } -impl BackendSystem for WinitBackend { - fn initialize(&mut self, window: WindowConfig) -> Result>, String> - where - S: 'static, - R: FnMut(&mut App, &mut S) -> Result + 'static, - { - let event_loop = EventLoop::new(); - let win = WinitWindowBackend::new(window, &event_loop)?; - let mut dpi_scale = win +fn add_event(b: &mut WinitBackend, request_redraw: &mut bool, evt: Event) { + b.events.push(evt); + *request_redraw = true; +} + +struct LoadState { + app_loader: Box, + window_config: WindowConfig, +} + +struct RunState { + app_runner: Box, + dpi_scale: f64, + mouse_x: i32, + mouse_y: i32, + request_redraw: bool, +} + +enum AppHandler { + Load(LoadState), + Run(RunState), + LoadFailed, +} + +impl LoadState { + fn load(mut self, event_loop: &ActiveEventLoop) -> Result { + let backend: &mut WinitBackend = self.app_loader.backend().downcast_mut().unwrap(); + let win = WinitWindowBackend::new(self.window_config, event_loop)?; + backend.window = Some(win); + let mut app_runner = self.app_loader.load()?; + let backend: &mut WinitBackend = app_runner.app_mut().backend().unwrap(); + let dpi_scale = backend + .window + .as_mut() + .unwrap() .window() .current_monitor() .as_ref() .map_or(1.0, |m| m.scale_factor()); - self.window = Some(win); + Ok(RunState { + app_runner, + dpi_scale, + mouse_x: 0, + mouse_y: 0, + request_redraw: false, + }) + } +} + +impl RunState { + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let app = self.app_runner.app_mut(); + let b = backend(&mut app.backend); + + // Await for the next event to run the loop again + let is_lazy = b.window.as_ref().is_some_and(|w| w.lazy); + + if let Some(evt) = + mouse::process_events(&event, &mut self.mouse_x, &mut self.mouse_y, self.dpi_scale) + { + add_event(b, &mut self.request_redraw, evt); + } + + if let Some(evt) = keyboard::process_events(&event) { + add_event(b, &mut self.request_redraw, evt); + } - Ok(Box::new(move |mut app: App, mut state: S, mut cb: R| { - let (mut mouse_x, mut mouse_y) = (0, 0); - let mut request_redraw = false; + keyboard::process_char_events(&event, |e| add_event(b, &mut self.request_redraw, e)); - let add_event = move |b: &mut WinitBackend, request_redraw: &mut bool, evt: Event| { - b.events.push(evt); - *request_redraw = true; - }; + if let Some(evt) = touch::process_events(&event, self.dpi_scale) { + add_event(b, &mut self.request_redraw, evt); + } - event_loop.run(move |event, _win_target, control_flow| { - let b = backend(&mut app.backend); + #[cfg(feature = "clipboard")] + if let Some(evt) = clipboard::process_events(&event, &app.keyboard) { + add_event(b, &mut self.request_redraw, evt); + } - // Await for the next event to run the loop again - let is_lazy = b.window.as_ref().map_or(false, |w| w.lazy); - if is_lazy { - *control_flow = ControlFlow::Wait; + match event { + WindowEvent::Touch(t) => { + println!("{t:?}"); + } + WindowEvent::CloseRequested => { + self.app_runner.app_mut().exit(); + } + WindowEvent::Resized(size) => { + if let Some(win) = &mut b.window { + win.resize(size.width, size.height); } - match event { - WEvent::WindowEvent { ref event, .. } => { - if let Some(evt) = - mouse::process_events(event, &mut mouse_x, &mut mouse_y, dpi_scale) - { - add_event(b, &mut request_redraw, evt); - } - - if let Some(evt) = keyboard::process_events(event) { - add_event(b, &mut request_redraw, evt); - } - - if let Some(evt) = touch::process_events(event, dpi_scale) { - add_event(b, &mut request_redraw, evt); - } - - #[cfg(feature = "clipboard")] - if let Some(evt) = clipboard::process_events(event, &app.keyboard) { - add_event(b, &mut request_redraw, evt); - } - - match event { - WindowEvent::Touch(t) => { - println!("{t:?}"); - } - WindowEvent::CloseRequested => { - app.exit(); - } - WindowEvent::Resized(size) => { - if let Some(win) = &mut b.window { - win.resize(size.width, size.height); - } - - let logical_size = size.to_logical::(dpi_scale); - add_event( - b, - &mut request_redraw, - Event::WindowResize { - width: logical_size.width as _, - height: logical_size.height as _, - }, - ); - } - WindowEvent::ScaleFactorChanged { - scale_factor, - new_inner_size: size, - } => { - if let Some(win) = &mut b.window { - win.resize(size.width, size.height); - dpi_scale = *scale_factor; - win.scale_factor = dpi_scale; - } - - let logical_size = size.to_logical::(dpi_scale); - - add_event( - b, - &mut request_redraw, - Event::ScreenAspectChange { ratio: dpi_scale }, - ); - add_event( - b, - &mut request_redraw, - Event::WindowResize { - width: logical_size.width as _, - height: logical_size.height as _, - }, - ); - } - WindowEvent::ReceivedCharacter(c) => { - add_event(b, &mut request_redraw, Event::ReceivedCharacter(*c)); - } - - #[cfg(feature = "drop_files")] - WindowEvent::HoveredFile(path) => { - let name = path.file_name().map_or_else( - || "".to_string(), - |n| n.to_string_lossy().to_string(), - ); - - let mime = mime_guess::from_path(path) - .first_raw() - .unwrap_or("") - .to_string(); - - add_event( - b, - &mut request_redraw, - Event::DragEnter { - path: Some(path.clone()), - name: Some(name), - mime, - }, - ); - } - #[cfg(feature = "drop_files")] - WindowEvent::HoveredFileCancelled => { - add_event(b, &mut request_redraw, Event::DragLeft); - } - #[cfg(feature = "drop_files")] - WindowEvent::DroppedFile(path) => { - let name = path - .file_name() - .map(|name| name.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_string()); - - let mime = mime_guess::from_path(path) - .first_raw() - .unwrap_or("") - .to_string(); - - add_event( - b, - &mut request_redraw, - Event::Drop(DroppedFile { - path: Some(path.clone()), - name, - mime, - }), - ); - } - - _ => {} - } - } - WEvent::MainEventsCleared => { - let needs_redraw = !is_lazy || request_redraw; - if needs_redraw { - if let Some(win) = &mut b.window { - win.window().request_redraw(); - } - } + let logical_size = size.to_logical::(self.dpi_scale); + add_event( + b, + &mut self.request_redraw, + Event::WindowResize { + width: logical_size.width as _, + height: logical_size.height as _, + }, + ); + } + WindowEvent::ScaleFactorChanged { scale_factor, .. } => { + if let Some(win) = &mut b.window { + self.dpi_scale = scale_factor; + win.scale_factor = self.dpi_scale; + } + + add_event( + b, + &mut self.request_redraw, + Event::ScreenAspectChange { + ratio: self.dpi_scale, + }, + ); + } + #[cfg(feature = "drop_files")] + WindowEvent::HoveredFile(path) => { + let name = path + .file_name() + .map_or_else(|| "".to_string(), |n| n.to_string_lossy().to_string()); + + let mime = mime_guess::from_path(&path) + .first_raw() + .unwrap_or("") + .to_string(); + + add_event( + b, + &mut self.request_redraw, + Event::DragEnter { + path: Some(path.clone()), + name: Some(name), + mime, + }, + ); + } + #[cfg(feature = "drop_files")] + WindowEvent::HoveredFileCancelled => { + add_event(b, &mut self.request_redraw, Event::DragLeft); + } + #[cfg(feature = "drop_files")] + WindowEvent::DroppedFile(path) => { + let name = path + .file_name() + .map(|name| name.to_string_lossy().to_string()) + .unwrap_or_else(|| "".to_string()); + + let mime = mime_guess::from_path(&path) + .first_raw() + .unwrap_or("") + .to_string(); + + add_event( + b, + &mut self.request_redraw, + Event::Drop(DroppedFile { + path: Some(path.clone()), + name, + mime, + }), + ); + } + WindowEvent::RedrawRequested => { + self.request_redraw = false; + if let Some(w) = &mut b.window { + w.frame_requested = false; + } + + match self.app_runner.run() { + Ok(FrameState::End) => { + backend(&mut self.app_runner.app_mut().backend) + .window + .as_mut() + .unwrap() + .swap_buffers(); } - WEvent::RedrawRequested(_) => { - match cb(&mut app, &mut state) { - Ok(FrameState::End) => { - backend(&mut app.backend) - .window - .as_mut() - .unwrap() - .swap_buffers(); - } - Ok(FrameState::Skip) => { - // log::debug!("Frame skipped"); - // no-op - } - Err(e) => { - log::error!("{}", e); - } - } + Ok(FrameState::Skip) => { + // log::debug!("Frame skipped"); + // no-op } - WEvent::RedrawEventsCleared => { - request_redraw = false; + Err(e) => { + log::error!("{e}"); } - _ => {} } + } - let b = backend(&mut app.backend); + _ => {} + } - // Close the loop if the user want to exit - let exit_requested = b.exit_requested; - if exit_requested { - *control_flow = ControlFlow::Exit; - } - }); - })) + if backend(&mut self.app_runner.app_mut().backend).exit_requested { + event_loop.exit(); + return; + } + let control_flow = { + if self.request_redraw { + // If something needs to be drawn keep polling events + ControlFlow::Poll + } else if is_lazy { + // If is in lazy mode and nothing needs to be drawn just wait + ControlFlow::Wait + } else { + // by default keep polling events + ControlFlow::Poll + } + }; + event_loop.set_control_flow(control_flow); + } + + fn device_event( + &mut self, + _event_loop: &winit::event_loop::ActiveEventLoop, + _device_id: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) { + if let Some(evt) = mouse::process_device_events(&event) { + let b = backend(&mut self.app_runner.app_mut().backend); + add_event(b, &mut self.request_redraw, evt); + } + } + + fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + let b = backend(&mut self.app_runner.app_mut().backend); + let is_lazy = b.window.as_ref().is_some_and(|w| w.lazy); + let needs_redraw = + !is_lazy || self.request_redraw || b.window.as_ref().is_some_and(|w| w.frame_requested); + if needs_redraw { + if let Some(win) = &mut b.window { + win.window().request_redraw(); + } + } + } +} + +impl ApplicationHandler for AppHandler { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if matches!(self, AppHandler::Load(_)) { + let ldr = core::mem::replace(self, AppHandler::LoadFailed); + match ldr { + AppHandler::Load(loader) => match loader.load(event_loop) { + Ok(runner) => *self = AppHandler::Run(runner), + Err(e) => { + log::error!("Failed to initialize app: {e}"); + event_loop.exit(); + } + }, + _ => unreachable!(), + } + } + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + window_id: winit::window::WindowId, + event: WindowEvent, + ) { + if let AppHandler::Run(st) = self { + st.window_event(event_loop, window_id, event) + } + } + + fn device_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + device_id: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) { + if let AppHandler::Run(st) = self { + st.device_event(event_loop, device_id, event) + } + } + + fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if let AppHandler::Run(st) = self { + st.about_to_wait(event_loop) + } + } +} + +struct WinitRunner; + +impl BackendRunner for WinitRunner { + fn run( + &mut self, + app_loader: Box, + window_config: WindowConfig, + ) -> Result<(), String> { + let event_loop = EventLoop::new().map_err(|e| e.to_string())?; + let mut handler = AppHandler::Load(LoadState { + app_loader, + window_config, + }); + event_loop.run_app(&mut handler).map_err(|e| e.to_string()) + } +} + +impl BackendSystem for WinitBackend { + fn runner(&self) -> Box { + Box::new(WinitRunner) } fn get_graphics_backend(&self) -> Box { diff --git a/crates/notan_winit/src/clipboard.rs b/crates/notan_winit/src/clipboard.rs index a10db123..f29f3967 100644 --- a/crates/notan_winit/src/clipboard.rs +++ b/crates/notan_winit/src/clipboard.rs @@ -2,14 +2,14 @@ use notan_core::events::Event; use notan_input::keyboard::Keyboard; -use winit::event::VirtualKeyCode; use winit::event::{ElementState, WindowEvent}; +use winit::keyboard::{KeyCode as WinitKeyCode, PhysicalKey}; pub fn process_events(event: &WindowEvent, keyboard: &Keyboard) -> Option { match event { - WindowEvent::KeyboardInput { input, .. } => { - if let Some(key) = input.virtual_keycode.as_ref() { - if input.state == ElementState::Pressed { + WindowEvent::KeyboardInput { event, .. } => { + if let PhysicalKey::Code(ref key) = event.physical_key { + if event.state == ElementState::Pressed { if is_cut(keyboard, key) { return Some(Event::Cut); } else if is_copy(keyboard, key) { @@ -34,7 +34,7 @@ pub fn process_events(event: &WindowEvent, keyboard: &Keyboard) -> Option pub fn set_clipboard_text(text: &str) { if let Some(mut clipboard) = init_arboard() { if let Err(err) = clipboard.set_text(text) { - log::error!("failed to set_text on clipboard: {}", err); + log::error!("failed to set_text on clipboard: {err}"); } } } @@ -44,7 +44,7 @@ fn get_clipboard_text() -> Option { return match clipboard.get_text() { Ok(text) => Some(text), Err(err) => { - log::error!("failed to get_text from clipboard: {}", err); + log::error!("failed to get_text from clipboard: {err}"); None } }; @@ -53,19 +53,19 @@ fn get_clipboard_text() -> Option { None } -fn is_cut(keyboard: &Keyboard, keycode: &VirtualKeyCode) -> bool { - is_command_pressed(keyboard) && *keycode == VirtualKeyCode::X - || (cfg!(target_os = "windows") && keyboard.shift() && *keycode == VirtualKeyCode::Delete) +fn is_cut(keyboard: &Keyboard, keycode: &WinitKeyCode) -> bool { + is_command_pressed(keyboard) && *keycode == WinitKeyCode::KeyX + || (cfg!(target_os = "windows") && keyboard.shift() && *keycode == WinitKeyCode::Delete) } -fn is_copy(keyboard: &Keyboard, keycode: &VirtualKeyCode) -> bool { - is_command_pressed(keyboard) && *keycode == VirtualKeyCode::C - || (cfg!(target_os = "windows") && keyboard.ctrl() && *keycode == VirtualKeyCode::Insert) +fn is_copy(keyboard: &Keyboard, keycode: &WinitKeyCode) -> bool { + is_command_pressed(keyboard) && *keycode == WinitKeyCode::KeyC + || (cfg!(target_os = "windows") && keyboard.ctrl() && *keycode == WinitKeyCode::Insert) } -fn is_paste(keyboard: &Keyboard, keycode: &VirtualKeyCode) -> bool { - is_command_pressed(keyboard) && *keycode == VirtualKeyCode::V - || (cfg!(target_os = "windows") && keyboard.shift() && *keycode == VirtualKeyCode::Insert) +fn is_paste(keyboard: &Keyboard, keycode: &WinitKeyCode) -> bool { + is_command_pressed(keyboard) && *keycode == WinitKeyCode::KeyV + || (cfg!(target_os = "windows") && keyboard.shift() && *keycode == WinitKeyCode::Insert) } // returns true for ⌘ Command on mac and ctrl on others @@ -83,7 +83,7 @@ fn init_arboard() -> Option { match arboard::Clipboard::new() { Ok(clipboard) => Some(clipboard), Err(err) => { - log::error!("failed to initialize clipboard: {}", err); + log::error!("failed to initialize clipboard: {err}"); None } } diff --git a/crates/notan_winit/src/gl_manager.rs b/crates/notan_winit/src/gl_manager.rs index 945ee0cd..8a7d6c64 100644 --- a/crates/notan_winit/src/gl_manager.rs +++ b/crates/notan_winit/src/gl_manager.rs @@ -1,18 +1,18 @@ use glutin::config::Config as GConfig; use glutin::config::{ConfigTemplateBuilder, GlConfig}; use glutin::context::{ - ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentGlContextSurfaceAccessor, - PossiblyCurrentContext, Version, + ContextApi, ContextAttributesBuilder, GlProfile, PossiblyCurrentContext, Version, }; use glutin::display::{Display, GetGlDisplay, GlDisplay}; +use glutin::prelude::NotCurrentGlContext; use glutin::surface::{GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface}; use glutin_winit::DisplayBuilder; use notan_app::WindowConfig; -use raw_window_handle::HasRawWindowHandle; +use raw_window_handle::HasWindowHandle; use std::num::NonZeroU32; -use winit::event_loop::EventLoop; +use winit::event_loop::ActiveEventLoop; use winit::window::Fullscreen::Borderless; -use winit::window::{Window, WindowBuilder}; +use winit::window::{Window, WindowAttributes}; enum GlSupport { Full(GConfig), @@ -29,8 +29,8 @@ pub(crate) struct GlManager { impl GlManager { pub fn new( - builder: WindowBuilder, - event_loop: &EventLoop<()>, + builder: WindowAttributes, + event_loop: &ActiveEventLoop, config: &WindowConfig, ) -> Result { let mut template = ConfigTemplateBuilder::new().with_transparency(config.transparent); @@ -45,35 +45,35 @@ impl GlManager { let needs_transparency = config.transparent; let (window, gl_config) = DisplayBuilder::new() - .with_window_builder(Some(builder)) + .with_window_attributes(Some(builder)) .build(event_loop, template, |configs| { let mut support: Option = None; - configs.into_iter().for_each(|new_conf| match &support { - Some(GlSupport::Full(conf)) => { - let is = check_support(needs_transparency, conf, &new_conf); - if is.full_support && is.more_samples { - support = Some(GlSupport::Full(new_conf)); + configs.into_iter().for_each(|conf| match &support { + Some(GlSupport::Full(_)) => { + let is = check_support(config.multisampling, needs_transparency, &conf); + if is.full_support && is.req_samples { + support = Some(GlSupport::Full(conf)); } } - Some(GlSupport::Srgba(conf)) => { - let is = check_support(needs_transparency, conf, &new_conf); + Some(GlSupport::Srgba(_)) => { + let is = check_support(config.multisampling, needs_transparency, &conf); if is.full_support { - support = Some(GlSupport::Full(new_conf)); - } else if is.srgb && is.more_samples { - support = Some(GlSupport::Srgba(new_conf)); + support = Some(GlSupport::Full(conf)); + } else if is.srgb && is.req_samples { + support = Some(GlSupport::Srgba(conf)); } } - Some(GlSupport::Any(conf)) => { - let is = check_support(needs_transparency, conf, &new_conf); + Some(GlSupport::Any(_)) => { + let is = check_support(config.multisampling, needs_transparency, &conf); if is.full_support { - support = Some(GlSupport::Full(new_conf)); + support = Some(GlSupport::Full(conf)); } else if is.srgb { - support = Some(GlSupport::Srgba(new_conf)); - } else if is.more_samples { - support = Some(GlSupport::Any(new_conf)); + support = Some(GlSupport::Srgba(conf)); + } else if is.req_samples { + support = Some(GlSupport::Any(conf)); } } - None => support = Some(GlSupport::Any(new_conf)), + None => support = Some(GlSupport::Any(conf)), }); match support { @@ -93,7 +93,11 @@ impl GlManager { format!("{err}: {e}") })?; - let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle()); + let raw_window_handle = window + .as_ref() + .map(|window| window.window_handle().map_err(|e| e.to_string())) + .transpose()? + .map(|handle| handle.as_raw()); let display = gl_config.display(); let context_attributes = ContextAttributesBuilder::new() @@ -115,7 +119,7 @@ impl GlManager { let window = window.ok_or_else(|| "Cannot create a Window for the GL Context.".to_string())?; let (width, height): (u32, u32) = window.inner_size().into(); - let raw_window_handle = window.raw_window_handle(); + let raw_window_handle = window.window_handle().map_err(|e| e.to_string())?.as_raw(); let attrs = SurfaceAttributesBuilder::::new() .with_srgb(Some(true)) .build( @@ -199,19 +203,15 @@ impl GlManager { } struct InnerSupport { - more_samples: bool, + req_samples: bool, srgb: bool, full_support: bool, } -fn check_support( - needs_transparency: bool, - current_conf: &GConfig, - new_conf: &GConfig, -) -> InnerSupport { - let more_samples = new_conf.num_samples() > current_conf.num_samples(); - let srgb = new_conf.srgb_capable(); - let supports_transparency = new_conf.supports_transparency().unwrap_or(false); +fn check_support(required_samples: u8, needs_transparency: bool, conf: &GConfig) -> InnerSupport { + let req_samples = conf.num_samples() == required_samples && required_samples != 0; + let srgb = conf.srgb_capable(); + let supports_transparency = conf.supports_transparency().unwrap_or(false); let transparency = if needs_transparency { supports_transparency } else { @@ -220,7 +220,7 @@ fn check_support( let full_support = srgb && transparency; InnerSupport { - more_samples, + req_samples, srgb, full_support, } diff --git a/crates/notan_winit/src/keyboard.rs b/crates/notan_winit/src/keyboard.rs index 54e087e1..1be37104 100644 --- a/crates/notan_winit/src/keyboard.rs +++ b/crates/notan_winit/src/keyboard.rs @@ -1,14 +1,14 @@ use notan_core::events::Event; use notan_core::keyboard::KeyCode; use winit::event::ElementState; -use winit::event::VirtualKeyCode; use winit::event::WindowEvent; +use winit::keyboard::{KeyCode as WinitKeyCode, PhysicalKey}; pub fn process_events(event: &WindowEvent) -> Option { match event { - WindowEvent::KeyboardInput { input, .. } => { - let key = keyboard_button_to_nae(input.virtual_keycode.as_ref()); - let evt = match input.state { + WindowEvent::KeyboardInput { event, .. } => { + let key = keyboard_button_to_nae(event.physical_key); + let evt = match event.state { ElementState::Pressed => Event::KeyDown { key }, _ => Event::KeyUp { key }, }; @@ -19,172 +19,216 @@ pub fn process_events(event: &WindowEvent) -> Option { } } -fn keyboard_button_to_nae(key: Option<&VirtualKeyCode>) -> KeyCode { +pub fn process_char_events(window_event: &WindowEvent, mut add_event: impl FnMut(Event)) { + if let WindowEvent::KeyboardInput { event, .. } = window_event { + if event.state.is_pressed() { + if let Some(s) = &event.text { + for c in s.chars() { + add_event(Event::ReceivedCharacter(c)); + } + } + } + } +} + +fn keyboard_button_to_nae(key: PhysicalKey) -> KeyCode { match key { - Some(k) => match k { - VirtualKeyCode::Key1 => KeyCode::Key1, - VirtualKeyCode::Key2 => KeyCode::Key2, - VirtualKeyCode::Key3 => KeyCode::Key3, - VirtualKeyCode::Key4 => KeyCode::Key4, - VirtualKeyCode::Key5 => KeyCode::Key5, - VirtualKeyCode::Key6 => KeyCode::Key6, - VirtualKeyCode::Key7 => KeyCode::Key7, - VirtualKeyCode::Key8 => KeyCode::Key8, - VirtualKeyCode::Key9 => KeyCode::Key9, - VirtualKeyCode::Key0 => KeyCode::Key0, - VirtualKeyCode::A => KeyCode::A, - VirtualKeyCode::B => KeyCode::B, - VirtualKeyCode::C => KeyCode::C, - VirtualKeyCode::D => KeyCode::D, - VirtualKeyCode::E => KeyCode::E, - VirtualKeyCode::F => KeyCode::F, - VirtualKeyCode::G => KeyCode::G, - VirtualKeyCode::H => KeyCode::H, - VirtualKeyCode::I => KeyCode::I, - VirtualKeyCode::J => KeyCode::J, - VirtualKeyCode::K => KeyCode::K, - VirtualKeyCode::L => KeyCode::L, - VirtualKeyCode::M => KeyCode::M, - VirtualKeyCode::N => KeyCode::N, - VirtualKeyCode::O => KeyCode::O, - VirtualKeyCode::P => KeyCode::P, - VirtualKeyCode::Q => KeyCode::Q, - VirtualKeyCode::R => KeyCode::R, - VirtualKeyCode::S => KeyCode::S, - VirtualKeyCode::T => KeyCode::T, - VirtualKeyCode::U => KeyCode::U, - VirtualKeyCode::V => KeyCode::V, - VirtualKeyCode::W => KeyCode::W, - VirtualKeyCode::X => KeyCode::X, - VirtualKeyCode::Y => KeyCode::Y, - VirtualKeyCode::Z => KeyCode::Z, - VirtualKeyCode::Escape => KeyCode::Escape, - VirtualKeyCode::F1 => KeyCode::F1, - VirtualKeyCode::F2 => KeyCode::F2, - VirtualKeyCode::F3 => KeyCode::F3, - VirtualKeyCode::F4 => KeyCode::F4, - VirtualKeyCode::F5 => KeyCode::F5, - VirtualKeyCode::F6 => KeyCode::F6, - VirtualKeyCode::F7 => KeyCode::F7, - VirtualKeyCode::F8 => KeyCode::F8, - VirtualKeyCode::F9 => KeyCode::F9, - VirtualKeyCode::F10 => KeyCode::F10, - VirtualKeyCode::F11 => KeyCode::F11, - VirtualKeyCode::F12 => KeyCode::F12, - VirtualKeyCode::F13 => KeyCode::F13, - VirtualKeyCode::F14 => KeyCode::F14, - VirtualKeyCode::F15 => KeyCode::F15, - VirtualKeyCode::F16 => KeyCode::F16, - VirtualKeyCode::F17 => KeyCode::F17, - VirtualKeyCode::F18 => KeyCode::F18, - VirtualKeyCode::F19 => KeyCode::F19, - VirtualKeyCode::F20 => KeyCode::F20, - VirtualKeyCode::F21 => KeyCode::F21, - VirtualKeyCode::F22 => KeyCode::F22, - VirtualKeyCode::F23 => KeyCode::F23, - VirtualKeyCode::F24 => KeyCode::F24, - VirtualKeyCode::Snapshot => KeyCode::Snapshot, - VirtualKeyCode::Scroll => KeyCode::Scroll, - VirtualKeyCode::Pause => KeyCode::Pause, - VirtualKeyCode::Insert => KeyCode::Insert, - VirtualKeyCode::Home => KeyCode::Home, - VirtualKeyCode::Delete => KeyCode::Delete, - VirtualKeyCode::End => KeyCode::End, - VirtualKeyCode::PageDown => KeyCode::PageDown, - VirtualKeyCode::PageUp => KeyCode::PageUp, - VirtualKeyCode::Left => KeyCode::Left, - VirtualKeyCode::Up => KeyCode::Up, - VirtualKeyCode::Right => KeyCode::Right, - VirtualKeyCode::Down => KeyCode::Down, - VirtualKeyCode::Back => KeyCode::Back, - VirtualKeyCode::Return => KeyCode::Return, - VirtualKeyCode::Space => KeyCode::Space, - VirtualKeyCode::Compose => KeyCode::Compose, - VirtualKeyCode::Caret => KeyCode::Caret, - VirtualKeyCode::Numlock => KeyCode::Numlock, - VirtualKeyCode::Numpad0 => KeyCode::Numpad0, - VirtualKeyCode::Numpad1 => KeyCode::Numpad1, - VirtualKeyCode::Numpad2 => KeyCode::Numpad2, - VirtualKeyCode::Numpad3 => KeyCode::Numpad3, - VirtualKeyCode::Numpad4 => KeyCode::Numpad4, - VirtualKeyCode::Numpad5 => KeyCode::Numpad5, - VirtualKeyCode::Numpad6 => KeyCode::Numpad6, - VirtualKeyCode::Numpad7 => KeyCode::Numpad7, - VirtualKeyCode::Numpad8 => KeyCode::Numpad8, - VirtualKeyCode::Numpad9 => KeyCode::Numpad9, - VirtualKeyCode::AbntC1 => KeyCode::AbntC1, - VirtualKeyCode::AbntC2 => KeyCode::AbntC2, - VirtualKeyCode::NumpadAdd => KeyCode::Add, - VirtualKeyCode::Apostrophe => KeyCode::Apostrophe, - VirtualKeyCode::Apps => KeyCode::Apps, - VirtualKeyCode::At => KeyCode::At, - VirtualKeyCode::Ax => KeyCode::Ax, - VirtualKeyCode::Backslash => KeyCode::Backslash, - VirtualKeyCode::Calculator => KeyCode::Calculator, - VirtualKeyCode::Capital => KeyCode::Capital, - VirtualKeyCode::Colon => KeyCode::Colon, - VirtualKeyCode::Comma => KeyCode::Comma, - VirtualKeyCode::Convert => KeyCode::Convert, - VirtualKeyCode::NumpadDecimal => KeyCode::Decimal, - VirtualKeyCode::NumpadDivide => KeyCode::Divide, - VirtualKeyCode::Equals => KeyCode::Equals, - VirtualKeyCode::Grave => KeyCode::Grave, - VirtualKeyCode::Kana => KeyCode::Kana, - VirtualKeyCode::Kanji => KeyCode::Kanji, - VirtualKeyCode::LAlt => KeyCode::LAlt, - VirtualKeyCode::LBracket => KeyCode::LBracket, - VirtualKeyCode::LControl => KeyCode::LControl, - VirtualKeyCode::LShift => KeyCode::LShift, - VirtualKeyCode::LWin => KeyCode::LWin, - VirtualKeyCode::Mail => KeyCode::Mail, - VirtualKeyCode::MediaSelect => KeyCode::MediaSelect, - VirtualKeyCode::MediaStop => KeyCode::MediaStop, - VirtualKeyCode::Minus => KeyCode::Minus, - VirtualKeyCode::NumpadMultiply => KeyCode::Multiply, - VirtualKeyCode::Mute => KeyCode::Mute, - VirtualKeyCode::MyComputer => KeyCode::MyComputer, - VirtualKeyCode::NavigateForward => KeyCode::NavigateForward, - VirtualKeyCode::NavigateBackward => KeyCode::NavigateBackward, - VirtualKeyCode::NextTrack => KeyCode::NextTrack, - VirtualKeyCode::NoConvert => KeyCode::NoConvert, - VirtualKeyCode::NumpadComma => KeyCode::NumpadComma, - VirtualKeyCode::NumpadEnter => KeyCode::NumpadEnter, - VirtualKeyCode::NumpadEquals => KeyCode::NumpadEquals, - VirtualKeyCode::OEM102 => KeyCode::OEM102, - VirtualKeyCode::Period => KeyCode::Period, - VirtualKeyCode::PlayPause => KeyCode::PlayPause, - VirtualKeyCode::Power => KeyCode::Power, - VirtualKeyCode::PrevTrack => KeyCode::PrevTrack, - VirtualKeyCode::RAlt => KeyCode::RAlt, - VirtualKeyCode::RBracket => KeyCode::RBracket, - VirtualKeyCode::RControl => KeyCode::RControl, - VirtualKeyCode::RShift => KeyCode::RShift, - VirtualKeyCode::RWin => KeyCode::RWin, - VirtualKeyCode::Semicolon => KeyCode::Semicolon, - VirtualKeyCode::Slash => KeyCode::Slash, - VirtualKeyCode::Sleep => KeyCode::Sleep, - VirtualKeyCode::Stop => KeyCode::Stop, - VirtualKeyCode::NumpadSubtract => KeyCode::Subtract, - VirtualKeyCode::Sysrq => KeyCode::Sysrq, - VirtualKeyCode::Tab => KeyCode::Tab, - VirtualKeyCode::Underline => KeyCode::Underline, - VirtualKeyCode::Unlabeled => KeyCode::Unlabeled, - VirtualKeyCode::VolumeDown => KeyCode::VolumeDown, - VirtualKeyCode::VolumeUp => KeyCode::VolumeUp, - VirtualKeyCode::Wake => KeyCode::Wake, - VirtualKeyCode::WebBack => KeyCode::WebBack, - VirtualKeyCode::WebFavorites => KeyCode::WebFavorites, - VirtualKeyCode::WebForward => KeyCode::WebForward, - VirtualKeyCode::WebHome => KeyCode::WebHome, - VirtualKeyCode::WebRefresh => KeyCode::WebRefresh, - VirtualKeyCode::WebSearch => KeyCode::WebSearch, - VirtualKeyCode::WebStop => KeyCode::WebStop, - VirtualKeyCode::Yen => KeyCode::Yen, - VirtualKeyCode::Copy => KeyCode::Copy, - VirtualKeyCode::Paste => KeyCode::Paste, - VirtualKeyCode::Cut => KeyCode::Cut, - VirtualKeyCode::Asterisk => KeyCode::Asterisk, - VirtualKeyCode::Plus => KeyCode::Plus, + PhysicalKey::Code(k) => match k { + WinitKeyCode::Backquote => KeyCode::Backquote, + WinitKeyCode::Backslash => KeyCode::Backslash, + WinitKeyCode::BracketLeft => KeyCode::BracketLeft, + WinitKeyCode::BracketRight => KeyCode::BracketRight, + WinitKeyCode::Comma => KeyCode::Comma, + WinitKeyCode::Digit0 => KeyCode::Digit0, + WinitKeyCode::Digit1 => KeyCode::Digit1, + WinitKeyCode::Digit2 => KeyCode::Digit2, + WinitKeyCode::Digit3 => KeyCode::Digit3, + WinitKeyCode::Digit4 => KeyCode::Digit4, + WinitKeyCode::Digit5 => KeyCode::Digit5, + WinitKeyCode::Digit6 => KeyCode::Digit6, + WinitKeyCode::Digit7 => KeyCode::Digit7, + WinitKeyCode::Digit8 => KeyCode::Digit8, + WinitKeyCode::Digit9 => KeyCode::Digit9, + WinitKeyCode::Equal => KeyCode::Equal, + WinitKeyCode::IntlBackslash => KeyCode::IntlBackslash, + WinitKeyCode::IntlRo => KeyCode::IntlRo, + WinitKeyCode::IntlYen => KeyCode::IntlYen, + WinitKeyCode::KeyA => KeyCode::KeyA, + WinitKeyCode::KeyB => KeyCode::KeyB, + WinitKeyCode::KeyC => KeyCode::KeyC, + WinitKeyCode::KeyD => KeyCode::KeyD, + WinitKeyCode::KeyE => KeyCode::KeyE, + WinitKeyCode::KeyF => KeyCode::KeyF, + WinitKeyCode::KeyG => KeyCode::KeyG, + WinitKeyCode::KeyH => KeyCode::KeyH, + WinitKeyCode::KeyI => KeyCode::KeyI, + WinitKeyCode::KeyJ => KeyCode::KeyJ, + WinitKeyCode::KeyK => KeyCode::KeyK, + WinitKeyCode::KeyL => KeyCode::KeyL, + WinitKeyCode::KeyM => KeyCode::KeyM, + WinitKeyCode::KeyN => KeyCode::KeyN, + WinitKeyCode::KeyO => KeyCode::KeyO, + WinitKeyCode::KeyP => KeyCode::KeyP, + WinitKeyCode::KeyQ => KeyCode::KeyQ, + WinitKeyCode::KeyR => KeyCode::KeyR, + WinitKeyCode::KeyS => KeyCode::KeyS, + WinitKeyCode::KeyT => KeyCode::KeyT, + WinitKeyCode::KeyU => KeyCode::KeyU, + WinitKeyCode::KeyV => KeyCode::KeyV, + WinitKeyCode::KeyW => KeyCode::KeyW, + WinitKeyCode::KeyX => KeyCode::KeyX, + WinitKeyCode::KeyY => KeyCode::KeyY, + WinitKeyCode::KeyZ => KeyCode::KeyZ, + WinitKeyCode::Minus => KeyCode::Minus, + WinitKeyCode::Period => KeyCode::Period, + WinitKeyCode::Quote => KeyCode::Quote, + WinitKeyCode::Semicolon => KeyCode::Semicolon, + WinitKeyCode::Slash => KeyCode::Slash, + WinitKeyCode::AltLeft => KeyCode::AltLeft, + WinitKeyCode::AltRight => KeyCode::AltRight, + WinitKeyCode::Backspace => KeyCode::Backspace, + WinitKeyCode::CapsLock => KeyCode::CapsLock, + WinitKeyCode::ContextMenu => KeyCode::ContextMenu, + WinitKeyCode::ControlLeft => KeyCode::ControlLeft, + WinitKeyCode::ControlRight => KeyCode::ControlRight, + WinitKeyCode::Enter => KeyCode::Enter, + WinitKeyCode::SuperLeft => KeyCode::SuperLeft, + WinitKeyCode::SuperRight => KeyCode::SuperRight, + WinitKeyCode::ShiftLeft => KeyCode::ShiftLeft, + WinitKeyCode::ShiftRight => KeyCode::ShiftRight, + WinitKeyCode::Space => KeyCode::Space, + WinitKeyCode::Tab => KeyCode::Tab, + WinitKeyCode::Convert => KeyCode::Convert, + WinitKeyCode::KanaMode => KeyCode::KanaMode, + WinitKeyCode::Lang1 => KeyCode::Lang1, + WinitKeyCode::Lang2 => KeyCode::Lang2, + WinitKeyCode::Lang3 => KeyCode::Lang3, + WinitKeyCode::Lang4 => KeyCode::Lang4, + WinitKeyCode::Lang5 => KeyCode::Lang5, + WinitKeyCode::NonConvert => KeyCode::NonConvert, + WinitKeyCode::Delete => KeyCode::Delete, + WinitKeyCode::End => KeyCode::End, + WinitKeyCode::Help => KeyCode::Help, + WinitKeyCode::Home => KeyCode::Home, + WinitKeyCode::Insert => KeyCode::Insert, + WinitKeyCode::PageDown => KeyCode::PageDown, + WinitKeyCode::PageUp => KeyCode::PageUp, + WinitKeyCode::ArrowDown => KeyCode::ArrowDown, + WinitKeyCode::ArrowLeft => KeyCode::ArrowLeft, + WinitKeyCode::ArrowRight => KeyCode::ArrowRight, + WinitKeyCode::ArrowUp => KeyCode::ArrowUp, + WinitKeyCode::NumLock => KeyCode::NumLock, + WinitKeyCode::Numpad0 => KeyCode::Numpad0, + WinitKeyCode::Numpad1 => KeyCode::Numpad1, + WinitKeyCode::Numpad2 => KeyCode::Numpad2, + WinitKeyCode::Numpad3 => KeyCode::Numpad3, + WinitKeyCode::Numpad4 => KeyCode::Numpad4, + WinitKeyCode::Numpad5 => KeyCode::Numpad5, + WinitKeyCode::Numpad6 => KeyCode::Numpad6, + WinitKeyCode::Numpad7 => KeyCode::Numpad7, + WinitKeyCode::Numpad8 => KeyCode::Numpad8, + WinitKeyCode::Numpad9 => KeyCode::Numpad9, + WinitKeyCode::NumpadAdd => KeyCode::NumpadAdd, + WinitKeyCode::NumpadBackspace => KeyCode::NumpadBackspace, + WinitKeyCode::NumpadClear => KeyCode::NumpadClear, + WinitKeyCode::NumpadClearEntry => KeyCode::NumpadClearEntry, + WinitKeyCode::NumpadComma => KeyCode::NumpadComma, + WinitKeyCode::NumpadDecimal => KeyCode::NumpadDecimal, + WinitKeyCode::NumpadDivide => KeyCode::NumpadDivide, + WinitKeyCode::NumpadEnter => KeyCode::NumpadEnter, + WinitKeyCode::NumpadEqual => KeyCode::NumpadEqual, + WinitKeyCode::NumpadHash => KeyCode::NumpadHash, + WinitKeyCode::NumpadMemoryAdd => KeyCode::NumpadMemoryAdd, + WinitKeyCode::NumpadMemoryClear => KeyCode::NumpadMemoryClear, + WinitKeyCode::NumpadMemoryRecall => KeyCode::NumpadMemoryRecall, + WinitKeyCode::NumpadMemoryStore => KeyCode::NumpadMemoryStore, + WinitKeyCode::NumpadMemorySubtract => KeyCode::NumpadMemorySubtract, + WinitKeyCode::NumpadMultiply => KeyCode::NumpadMultiply, + WinitKeyCode::NumpadParenLeft => KeyCode::NumpadParenLeft, + WinitKeyCode::NumpadParenRight => KeyCode::NumpadParenRight, + WinitKeyCode::NumpadStar => KeyCode::NumpadStar, + WinitKeyCode::NumpadSubtract => KeyCode::NumpadSubtract, + WinitKeyCode::Escape => KeyCode::Escape, + WinitKeyCode::Fn => KeyCode::Fn, + WinitKeyCode::FnLock => KeyCode::FnLock, + WinitKeyCode::PrintScreen => KeyCode::PrintScreen, + WinitKeyCode::ScrollLock => KeyCode::ScrollLock, + WinitKeyCode::Pause => KeyCode::Pause, + WinitKeyCode::BrowserBack => KeyCode::BrowserBack, + WinitKeyCode::BrowserFavorites => KeyCode::BrowserFavorites, + WinitKeyCode::BrowserForward => KeyCode::BrowserForward, + WinitKeyCode::BrowserHome => KeyCode::BrowserHome, + WinitKeyCode::BrowserRefresh => KeyCode::BrowserRefresh, + WinitKeyCode::BrowserSearch => KeyCode::BrowserSearch, + WinitKeyCode::BrowserStop => KeyCode::BrowserStop, + WinitKeyCode::Eject => KeyCode::Eject, + WinitKeyCode::LaunchApp1 => KeyCode::LaunchApp1, + WinitKeyCode::LaunchApp2 => KeyCode::LaunchApp2, + WinitKeyCode::LaunchMail => KeyCode::LaunchMail, + WinitKeyCode::MediaPlayPause => KeyCode::MediaPlayPause, + WinitKeyCode::MediaSelect => KeyCode::MediaSelect, + WinitKeyCode::MediaStop => KeyCode::MediaStop, + WinitKeyCode::MediaTrackNext => KeyCode::MediaTrackNext, + WinitKeyCode::MediaTrackPrevious => KeyCode::MediaTrackPrevious, + WinitKeyCode::Power => KeyCode::Power, + WinitKeyCode::Sleep => KeyCode::Sleep, + WinitKeyCode::AudioVolumeDown => KeyCode::AudioVolumeDown, + WinitKeyCode::AudioVolumeMute => KeyCode::AudioVolumeMute, + WinitKeyCode::AudioVolumeUp => KeyCode::AudioVolumeUp, + WinitKeyCode::WakeUp => KeyCode::WakeUp, + WinitKeyCode::Meta => KeyCode::Meta, + WinitKeyCode::Hyper => KeyCode::Hyper, + WinitKeyCode::Turbo => KeyCode::Turbo, + WinitKeyCode::Abort => KeyCode::Abort, + WinitKeyCode::Resume => KeyCode::Resume, + WinitKeyCode::Suspend => KeyCode::Suspend, + WinitKeyCode::Again => KeyCode::Again, + WinitKeyCode::Copy => KeyCode::Copy, + WinitKeyCode::Cut => KeyCode::Cut, + WinitKeyCode::Find => KeyCode::Find, + WinitKeyCode::Open => KeyCode::Open, + WinitKeyCode::Paste => KeyCode::Paste, + WinitKeyCode::Props => KeyCode::Props, + WinitKeyCode::Select => KeyCode::Select, + WinitKeyCode::Undo => KeyCode::Undo, + WinitKeyCode::Hiragana => KeyCode::Hiragana, + WinitKeyCode::Katakana => KeyCode::Katakana, + WinitKeyCode::F1 => KeyCode::F1, + WinitKeyCode::F2 => KeyCode::F2, + WinitKeyCode::F3 => KeyCode::F3, + WinitKeyCode::F4 => KeyCode::F4, + WinitKeyCode::F5 => KeyCode::F5, + WinitKeyCode::F6 => KeyCode::F6, + WinitKeyCode::F7 => KeyCode::F7, + WinitKeyCode::F8 => KeyCode::F8, + WinitKeyCode::F9 => KeyCode::F9, + WinitKeyCode::F10 => KeyCode::F10, + WinitKeyCode::F11 => KeyCode::F11, + WinitKeyCode::F12 => KeyCode::F12, + WinitKeyCode::F13 => KeyCode::F13, + WinitKeyCode::F14 => KeyCode::F14, + WinitKeyCode::F15 => KeyCode::F15, + WinitKeyCode::F16 => KeyCode::F16, + WinitKeyCode::F17 => KeyCode::F17, + WinitKeyCode::F18 => KeyCode::F18, + WinitKeyCode::F19 => KeyCode::F19, + WinitKeyCode::F20 => KeyCode::F20, + WinitKeyCode::F21 => KeyCode::F21, + WinitKeyCode::F22 => KeyCode::F22, + WinitKeyCode::F23 => KeyCode::F23, + WinitKeyCode::F24 => KeyCode::F24, + WinitKeyCode::F25 => KeyCode::F25, + WinitKeyCode::F26 => KeyCode::F26, + WinitKeyCode::F27 => KeyCode::F27, + WinitKeyCode::F28 => KeyCode::F28, + WinitKeyCode::F29 => KeyCode::F29, + WinitKeyCode::F30 => KeyCode::F30, + WinitKeyCode::F31 => KeyCode::F31, + WinitKeyCode::F32 => KeyCode::F32, + WinitKeyCode::F33 => KeyCode::F33, + WinitKeyCode::F34 => KeyCode::F34, + WinitKeyCode::F35 => KeyCode::F35, + _ => KeyCode::Unknown, }, _ => KeyCode::Unknown, } diff --git a/crates/notan_winit/src/lib.rs b/crates/notan_winit/src/lib.rs index 2061c8e3..3e3bb8c2 100644 --- a/crates/notan_winit/src/lib.rs +++ b/crates/notan_winit/src/lib.rs @@ -9,4 +9,3 @@ mod gl_manager; pub mod prelude; pub use backend::*; -pub use notan_glow::texture_source::*; diff --git a/crates/notan_winit/src/mouse.rs b/crates/notan_winit/src/mouse.rs index 9a4998fd..f6e2f120 100644 --- a/crates/notan_winit/src/mouse.rs +++ b/crates/notan_winit/src/mouse.rs @@ -3,7 +3,7 @@ use winit::dpi::LogicalPosition; use notan_core::events::Event; use notan_core::mouse::MouseButton; use winit::event::ElementState; -use winit::event::{MouseButton as WMouseButton, MouseScrollDelta, WindowEvent}; +use winit::event::{DeviceEvent, MouseButton as WMouseButton, MouseScrollDelta, WindowEvent}; pub fn process_events( event: &WindowEvent, @@ -61,6 +61,14 @@ pub fn process_events( *my = position.y as _; Some(Event::MouseMove { x: *mx, y: *my }) } + + _ => None, + } +} + +pub fn process_device_events(event: &DeviceEvent) -> Option { + match event { + DeviceEvent::MouseMotion { delta } => Some(Event::MouseMotion { delta: *delta }), _ => None, } } @@ -70,6 +78,8 @@ fn mouse_button_to_nae(btn: &WMouseButton) -> MouseButton { WMouseButton::Left => MouseButton::Left, WMouseButton::Right => MouseButton::Right, WMouseButton::Middle => MouseButton::Middle, - WMouseButton::Other(n) => MouseButton::Other(*n as _), + WMouseButton::Back => MouseButton::Back, + WMouseButton::Forward => MouseButton::Forward, + WMouseButton::Other(n) => MouseButton::Other(*n), } } diff --git a/crates/notan_winit/src/window.rs b/crates/notan_winit/src/window.rs index 2a136e68..5781777c 100644 --- a/crates/notan_winit/src/window.rs +++ b/crates/notan_winit/src/window.rs @@ -3,10 +3,10 @@ use std::path::PathBuf; use crate::gl_manager::GlManager; use notan_app::WindowConfig; use notan_app::{CursorIcon, WindowBackend}; -use winit::dpi::{LogicalSize, PhysicalPosition}; -use winit::event_loop::EventLoop; +use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition}; +use winit::event_loop::ActiveEventLoop; use winit::window::Fullscreen::Borderless; -use winit::window::{CursorGrabMode, CursorIcon as WCursorIcon, Icon, Window, WindowBuilder}; +use winit::window::{CursorGrabMode, CursorIcon as WCursorIcon, Icon, Window, WindowLevel}; pub struct WinitWindowBackend { pub(crate) gl_manager: GlManager, @@ -15,11 +15,11 @@ pub struct WinitWindowBackend { cursor: CursorIcon, captured: bool, visible: bool, - high_dpi: bool, is_always_on_top: bool, mouse_passthrough: bool, title: String, use_touch_as_mouse: bool, + pub(crate) frame_requested: bool, } impl WindowBackend for WinitWindowBackend { @@ -32,10 +32,6 @@ impl WindowBackend for WinitWindowBackend { } fn dpi(&self) -> f64 { - if cfg!(target_os = "macos") && !self.high_dpi { - return 1.0; - } - self.scale_factor } @@ -51,6 +47,10 @@ impl WindowBackend for WinitWindowBackend { self.window().fullscreen().is_some() } + fn is_focused(&self) -> bool { + self.window().has_focus() + } + fn lazy_loop(&self) -> bool { self.lazy } @@ -67,6 +67,7 @@ impl WindowBackend for WinitWindowBackend { fn request_frame(&mut self) { if self.lazy { self.window().request_redraw(); + self.frame_requested = true; } } @@ -81,7 +82,12 @@ impl WindowBackend for WinitWindowBackend { } fn set_always_on_top(&mut self, enabled: bool) { - self.window().set_always_on_top(enabled); + let level = if enabled { + WindowLevel::AlwaysOnTop + } else { + WindowLevel::Normal + }; + self.window().set_window_level(level); self.is_always_on_top = enabled; } @@ -116,12 +122,21 @@ impl WindowBackend for WinitWindowBackend { } Some(icon) => { self.window().set_cursor_visible(true); - self.window().set_cursor_icon(icon); + self.window().set_cursor(icon); } } } } + fn set_cursor_position(&mut self, x: f32, y: f32) { + if let Err(e) = self + .window() + .set_cursor_position(LogicalPosition::new(x, y)) + { + log::error!("Error setting mouse cursor position to x: {x} y: {y} error: {e}"); + } + } + fn set_fullscreen(&mut self, enabled: bool) { if enabled { let monitor = self.window().current_monitor(); @@ -148,9 +163,10 @@ impl WindowBackend for WinitWindowBackend { .set_outer_position(PhysicalPosition::new(x, y)); } - fn set_size(&mut self, width: i32, height: i32) { - self.window() - .set_inner_size(LogicalSize::new(width, height)); + fn set_size(&mut self, width: u32, height: u32) { + let _ = self + .window() + .request_inner_size(LogicalSize::new(width, height)); } fn set_visible(&mut self, visible: bool) { @@ -160,7 +176,7 @@ impl WindowBackend for WinitWindowBackend { } } - fn size(&self) -> (i32, i32) { + fn size(&self) -> (u32, u32) { let inner = self.window().inner_size(); let logical = inner.to_logical::(self.scale_factor); (logical.width as _, logical.height as _) @@ -226,14 +242,19 @@ fn load_icon_from_data(data: &'static [u8]) -> Icon { } impl WinitWindowBackend { - pub(crate) fn new(config: WindowConfig, event_loop: &EventLoop<()>) -> Result { - let mut builder = WindowBuilder::new() + pub(crate) fn new(config: WindowConfig, event_loop: &ActiveEventLoop) -> Result { + let level = if config.always_on_top { + WindowLevel::AlwaysOnTop + } else { + WindowLevel::Normal + }; + let mut builder = Window::default_attributes() .with_title(&config.title) .with_inner_size(LogicalSize::new(config.width, config.height)) .with_maximized(config.maximized) .with_resizable(config.resizable) .with_transparent(config.transparent) - .with_always_on_top(config.always_on_top) + .with_window_level(level) .with_visible(config.visible) .with_decorations(config.decorations) .with_window_icon(load_icon( @@ -243,7 +264,7 @@ impl WinitWindowBackend { #[cfg(target_os = "windows")] { - use winit::platform::windows::WindowBuilderExtWindows; + use winit::platform::windows::WindowAttributesExtWindows; builder = builder.with_taskbar_icon(load_icon( &config.taskbar_icon_path, &config.taskbar_icon_data, @@ -252,10 +273,22 @@ impl WinitWindowBackend { #[cfg(target_os = "macos")] { - use winit::platform::macos::WindowBuilderExtMacOS; + use winit::platform::macos::WindowAttributesExtMacOS; builder = builder.with_disallow_hidpi(!config.high_dpi); } + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + use winit::platform::wayland::WindowAttributesExtWayland; + builder = builder.with_name(config.app_id.clone(), config.app_id.clone()); + } + if let Some((w, h)) = config.min_size { builder = builder.with_min_inner_size(LogicalSize::new(w, h)); } @@ -264,6 +297,24 @@ impl WinitWindowBackend { builder = builder.with_max_inner_size(LogicalSize::new(w, h)); } + if let Some((x, y)) = config.position { + #[cfg(not(windows))] + let (safe_x, safe_y) = (x, y); + + // This is already done by the OS in Linux/MacOS + // Winit no longer allows getting monitors from the event loop, so commenting this out + // until the code can be refactored. + #[cfg(windows)] + let (safe_x, safe_y) = { + let clamped_position = + clamp_window_to_sane_position(config.width, config.height, x, y, event_loop); + + (clamped_position.0, clamped_position.1) + }; + + builder = builder.with_position(LogicalPosition::new(safe_x as f64, safe_y as f64)); + } + let gl_manager = GlManager::new(builder, event_loop, &config)?; // Try setting vsync. @@ -287,7 +338,6 @@ impl WinitWindowBackend { let WindowConfig { lazy_loop, visible, - high_dpi, title, mouse_passthrough, .. @@ -300,11 +350,11 @@ impl WinitWindowBackend { cursor: CursorIcon::Default, captured: false, visible, - high_dpi, is_always_on_top: false, mouse_passthrough, title, use_touch_as_mouse: false, + frame_requested: false, }) } @@ -327,7 +377,7 @@ fn winit_cursor(cursor: CursorIcon) -> Option { CursorIcon::Default => WCursorIcon::Default, CursorIcon::ContextMenu => WCursorIcon::ContextMenu, CursorIcon::Help => WCursorIcon::Help, - CursorIcon::PointingHand => WCursorIcon::Hand, + CursorIcon::PointingHand => WCursorIcon::Pointer, CursorIcon::Progress => WCursorIcon::Progress, CursorIcon::Wait => WCursorIcon::Wait, CursorIcon::Cell => WCursorIcon::Cell, @@ -360,3 +410,65 @@ fn winit_cursor(cursor: CursorIcon) -> Option { CursorIcon::ResizeRow => WCursorIcon::RowResize, }) } + +#[cfg(windows)] +fn clamp_window_to_sane_position( + width: u32, + height: u32, + x: i32, + y: i32, + event_loop: &ActiveEventLoop, +) -> (i32, i32) { + let monitors = event_loop.available_monitors(); + // default to primary monitor, in case the correct monitor was disconnected. + let mut active_monitor = if let Some(active_monitor) = event_loop + .primary_monitor() + .or_else(|| event_loop.available_monitors().next()) + { + active_monitor + } else { + return (x, y); // no monitors 🤷 + }; + + for monitor in monitors { + let monitor_x_range = (monitor.position().x - width as i32) + ..(monitor.position().x + monitor.size().width as i32); + let monitor_y_range = (monitor.position().y - height as i32) + ..(monitor.position().y + monitor.size().height as i32); + + if monitor_x_range.contains(&x) && monitor_y_range.contains(&y) { + active_monitor = monitor; + } + } + + let mut inner_size_pixels = ( + width as f32 * active_monitor.scale_factor() as f32, + height as f32 * active_monitor.scale_factor() as f32, + ); + + // Add size of title bar. This is 32 px by default in Win 10/11. + if cfg!(target_os = "windows") { + inner_size_pixels.1 += 32.0 * active_monitor.scale_factor() as f32; + } + + let monitor_position = ( + active_monitor.position().x as f32, + active_monitor.position().y as f32, + ); + + let monitor_size = active_monitor.size(); + + // To get the maximum position, we get the rightmost corner of the display, then subtract + // the size of the window to get the bottom right most value window.position can have. + + let clamped_x = x.clamp( + monitor_position.0 as i32, + monitor_position.0 as i32 + monitor_size.width as i32 - inner_size_pixels.0 as i32, + ); + let clamped_y = y.clamp( + monitor_position.1 as i32, + monitor_position.1 as i32 + monitor_size.height as i32 - inner_size_pixels.1 as i32, + ); + + (clamped_x, clamped_y) +} diff --git a/examples/app_open_links.rs b/examples/app_open_links.rs index 93f7ae40..06beb7fb 100644 --- a/examples/app_open_links.rs +++ b/examples/app_open_links.rs @@ -23,9 +23,9 @@ fn setup(gfx: &mut Graphics) -> State { } fn update(app: &mut App) { - if app.keyboard.was_pressed(KeyCode::N) { + if app.keyboard.was_pressed(KeyCode::KeyN) { app.open_link("https://github.com/Nazariglez/notan"); - } else if app.keyboard.was_pressed(KeyCode::R) { + } else if app.keyboard.was_pressed(KeyCode::KeyR) { app.open_link("https://www.rust-lang.org"); } } diff --git a/examples/audio_basic.rs b/examples/audio_basic.rs index 8db9b5f4..df464135 100644 --- a/examples/audio_basic.rs +++ b/examples/audio_basic.rs @@ -62,7 +62,7 @@ fn set_volume(index: usize, app: &mut App, state: &mut State) { fn main() -> Result<(), String> { notan::init_with(setup) .add_config(EguiConfig) - .add_config(WindowConfig::default().size(300, 300)) + .add_config(WindowConfig::default().set_size(300, 300)) .draw(draw) .build() } diff --git a/examples/draw_animation_list.rs b/examples/draw_animation_list.rs index 3a17fab9..9176557f 100644 --- a/examples/draw_animation_list.rs +++ b/examples/draw_animation_list.rs @@ -47,10 +47,10 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { // An animation list take a &[&Texture] as frames draw.animation_list(&state.textures.iter().collect::>()) - // just a matrix translation - .translate(300.0, 200.0) // just a matrix scale .scale(2.0, 2.0) + // just a matrix translation + .translate(300.0, 200.0) // normalized frame time .time(state.time); diff --git a/examples/draw_blend_mode_object.rs b/examples/draw_blend_mode_object.rs index 1ebc28f4..a5f9756e 100644 --- a/examples/draw_blend_mode_object.rs +++ b/examples/draw_blend_mode_object.rs @@ -72,8 +72,8 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { // Draw image with a custom blend mode draw.image(&state.texture) - .translate(xx, yy) .scale(scale, scale) + .translate(xx, yy) .blend_mode(*mode); // print names diff --git a/examples/draw_bunnymark.rs b/examples/draw_bunnymark.rs index ba5862e5..8dbfd0a7 100644 --- a/examples/draw_bunnymark.rs +++ b/examples/draw_bunnymark.rs @@ -39,7 +39,10 @@ impl State { (0..n).for_each(|_| { self.bunnies.push(Bunny { pos: Vec2::ZERO, - speed: vec2(self.rng.gen_range(0.0..10.0), self.rng.gen_range(-5.0..5.0)), + speed: vec2( + self.rng.random_range(0.0..10.0), + self.rng.random_range(-5.0..5.0), + ), }) }); } @@ -72,8 +75,8 @@ fn update(app: &mut App, state: &mut State) { if b.pos.y > 600.0 { b.speed.y *= -0.85; b.pos.y = 600.0; - if rng.gen::() { - b.speed.y -= rng.gen_range(0.0..6.0); + if rng.random::() { + b.speed.y -= rng.random_range(0.0..6.0); } } else if b.pos.y < 0.0 { b.speed.y = 0.0; @@ -107,7 +110,7 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { #[notan_main] fn main() -> Result<(), String> { notan::init_with(init) - .add_config(WindowConfig::new().vsync(true)) + .add_config(WindowConfig::new().set_vsync(true)) .add_config(DrawConfig) .update(update) .draw(draw) diff --git a/examples/draw_mask_animated.rs b/examples/draw_mask_animated.rs index e71a5fb5..0b78e469 100644 --- a/examples/draw_mask_animated.rs +++ b/examples/draw_mask_animated.rs @@ -2,13 +2,13 @@ use notan::{draw::*, prelude::*}; fn main() { notan::init() - .add_config(WindowConfig::new().size(500, 500)) + .add_config(WindowConfig::new().set_size(500, 500)) .add_config(DrawConfig) .draw(|app: &mut App, gfx: &mut Graphics| { let mut draw = gfx.create_draw(); draw.clear(Color::BLACK); - let elapsed = app.timer.time_since_init(); + let elapsed = app.timer.elapsed_f32(); let pulse_progress = ((elapsed % 4.) - 2.).abs() / 2.; // 4s roundtrip pulse let radius = 200. * (1. - pulse_progress); diff --git a/examples/draw_path_flower.rs b/examples/draw_path_flower.rs index eb8fa544..a19d802c 100644 --- a/examples/draw_path_flower.rs +++ b/examples/draw_path_flower.rs @@ -17,14 +17,14 @@ const PI: f32 = std::f32::consts::PI; #[notan_main] fn main() -> Result<(), String> { notan::init() - .add_config(WindowConfig::new().multisampling(8)) + .add_config(WindowConfig::new().set_multisampling(8)) .add_config(DrawConfig) .draw(draw) .build() } fn draw(app: &mut App, gfx: &mut Graphics) { - let time = app.timer.time_since_init() * 1000.0; + let time = app.timer.elapsed_f32() * 1000.0; let mut draw = gfx.create_draw(); draw.clear(Color::BLACK); diff --git a/examples/draw_points.rs b/examples/draw_points.rs new file mode 100644 index 00000000..826747c5 --- /dev/null +++ b/examples/draw_points.rs @@ -0,0 +1,108 @@ +use notan::draw::*; +use notan::prelude::*; + +#[derive(AppState)] +struct State { + #[cfg(feature = "notan_text")] + font: Font, +} + +#[notan_main] +fn main() -> Result<(), String> { + let win_config = WindowConfig::default().set_size(380, 380); + notan::init_with(setup) + .add_config(DrawConfig) + .add_config(win_config) + .draw(draw) + .build() +} + +fn setup(gfx: &mut Graphics) -> State { + #[cfg(feature = "notan_text")] + { + let font = gfx + .create_font(include_bytes!("./assets/Ubuntu-B.ttf")) + .unwrap(); + State { font } + } + #[cfg(not(feature = "notan_text"))] + State {} +} + +fn draw(gfx: &mut Graphics, state: &mut State) { + let mut draw = gfx.create_draw(); + draw.clear(Color::BLACK); + + let (xpad, ypad) = (20., 20.); // padding + let (mut xal, mut yal); // aligment + + for xsquare in 0..=2 { + xal = match xsquare { + 0 => XAlignment::Left, + 1 => XAlignment::Center, + _ => XAlignment::Right, + }; + let xpos = xpad + xpad * xsquare as f32 + xsquare as f32 * 100.; + + #[cfg(feature = "notan_text")] + draw.text(&state.font, &format!["X:{xal:?}"]) + .position(xpos + 50., 5.0) + .color(Color::WHITE) + .size(14.0) + .alpha(0.7) + .h_align_center() + .v_align_middle(); + + for ysquare in 0..=2 { + yal = match ysquare { + 0 => YAlignment::Top, + 1 => YAlignment::Center, + _ => YAlignment::Bottom, + }; + let ypos = ypad + ypad * ysquare as f32 + ysquare as f32 * 100.; + + // draw each gray rectangle background + draw.rect((xpos, ypos), (100.0, 100.0)) + .fill_color(Color::new(0.3, 0.3, 0.3, 1.)); + + #[cfg(feature = "notan_text")] + if xsquare == 0 { + draw.text(&state.font, &format!["Y:\n{yal:?}"]) + .position(0.0, ypos + 50.) + .color(Color::WHITE) + .size(14.0) + .alpha(0.7) + .h_align_left() + .v_align_middle(); + } + + // draw the inner rectangle points + for x in 0..=9 { + for y in 0..=9 { + #[cfg(feature = "notan_text")] + if xsquare == 0 && x == 0 { + draw.text(&state.font, &format!["{}", 1 + y]) + .position(370., ypos + y as f32 * 10.) + .color(Color::WHITE) + .size(12.0) + .h_align_center() + .v_align_middle(); + } + let (x, y) = (x as f32, y as f32); + + // point increasing in width vertically + draw.point(xpos + x * 10., ypos + y * 10.) + .alpha(0.5) + .width(1.0 + y) + .align(xal, yal) + .color(Color::new(0.9, 0.9, 0.9, 0.9)); + // origin point in red + draw.point(xpos + x * 10., ypos + y * 10.) + .alpha(0.5) + .color(Color::RED); + } + } + } + } + gfx.render(&draw); +} diff --git a/examples/draw_projection.rs b/examples/draw_projection.rs index 9f1f1a5e..798576cd 100644 --- a/examples/draw_projection.rs +++ b/examples/draw_projection.rs @@ -8,7 +8,7 @@ const WORK_SIZE: Vec2 = vec2(800.0, 600.0); #[notan_main] fn main() -> Result<(), String> { - let win_config = WindowConfig::default().resizable(true); + let win_config = WindowConfig::default().set_resizable(true); notan::init() .add_config(win_config) diff --git a/examples/draw_transform_local.rs b/examples/draw_transform_local.rs index d095e123..08d1168d 100644 --- a/examples/draw_transform_local.rs +++ b/examples/draw_transform_local.rs @@ -27,17 +27,17 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { draw.rect((0.0, 0.0), (100.0, 100.0)) .color(Color::AQUA) - // Matrix translation - .translate(220.0, 220.0) // Helper to pivot from a point using degrees - .rotate_degrees_from((50.0, 50.0), state.rot); + .rotate_degrees_from((50.0, 50.0), state.rot) + // Matrix translation + .translate(220.0, 220.0); draw.circle(20.0) .color(Color::ORANGE) - // Matrix translation - .translate(500.0, 320.0) // Helper to scale from a point - .scale_from((0.0, 0.0), (2.0 + n.sin(), 2.0 + n.cos())); + .scale_from((0.0, 0.0), (2.0 + n.sin(), 2.0 + n.cos())) + // Matrix translation + .translate(500.0, 320.0); // Create a matrix that we can set to the next paint let translation = Mat3::from_translation(Vec2::new(200.0, 400.0)); diff --git a/examples/egui_basic.rs b/examples/egui_basic.rs index d3f24d85..e04c7081 100644 --- a/examples/egui_basic.rs +++ b/examples/egui_basic.rs @@ -4,9 +4,9 @@ use notan::prelude::*; #[notan_main] fn main() -> Result<(), String> { let win = WindowConfig::new() - .vsync(true) - .lazy_loop(true) - .high_dpi(true); + .set_vsync(true) + .set_lazy_loop(true) + .set_high_dpi(true); notan::init() .add_config(win) @@ -34,8 +34,5 @@ fn draw(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins) { }); output.clear_color(Color::BLACK); - - if output.needs_repaint() { - gfx.render(&output); - } + gfx.render(&output); } diff --git a/examples/egui_custom_font.rs b/examples/egui_custom_font.rs index 77a880ea..38ccf3ac 100644 --- a/examples/egui_custom_font.rs +++ b/examples/egui_custom_font.rs @@ -1,12 +1,14 @@ +use std::sync::Arc; + use notan::egui::{self, *}; use notan::prelude::*; #[notan_main] fn main() -> Result<(), String> { let win = WindowConfig::new() - .vsync(true) - .lazy_loop(true) - .high_dpi(true); + .set_vsync(true) + .set_lazy_loop(true) + .set_high_dpi(true); notan::init() .add_config(win) @@ -25,10 +27,7 @@ fn draw(gfx: &mut Graphics, plugins: &mut Plugins) { }); output.clear_color(Color::BLACK); - - if output.needs_repaint() { - gfx.render(&output); - } + gfx.render(&output); } // Initialize callback is called just once after setup and before the app's loop @@ -45,7 +44,9 @@ fn setup(ctx: &egui::Context) { // .ttf and .otf files supported. fonts.font_data.insert( "my_font".to_owned(), - egui::FontData::from_static(include_bytes!("./assets/Ubuntu-B.ttf")), + Arc::new(egui::FontData::from_static(include_bytes!( + "./assets/Ubuntu-B.ttf" + ))), ); // Put my font first (highest priority) for proportional text: diff --git a/examples/egui_demo.rs b/examples/egui_demo.rs index a61f831e..6e13e045 100644 --- a/examples/egui_demo.rs +++ b/examples/egui_demo.rs @@ -9,12 +9,12 @@ struct State { #[notan_main] fn main() -> Result<(), String> { let win = WindowConfig::default() - .resizable(true) - .size(1280, 1024) - .vsync(true) - .high_dpi(true) + .set_resizable(true) + .set_size(1280, 1024) + .set_vsync(true) + .set_high_dpi(true) // enable lazy mode to only draw after an input - .lazy_loop(true); + .set_lazy_loop(true); notan::init_with(State::default) .add_config(win) @@ -26,8 +26,5 @@ fn main() -> Result<(), String> { fn draw(gfx: &mut Graphics, plugins: &mut Plugins, state: &mut State) { let mut output = plugins.egui(|ctx| state.demo.ui(ctx)); output.clear_color(Color::BLACK); - - if output.needs_repaint() { - gfx.render(&output); - } + gfx.render(&output); } diff --git a/examples/egui_paint.rs b/examples/egui_paint.rs index 1cd61d20..5019565a 100644 --- a/examples/egui_paint.rs +++ b/examples/egui_paint.rs @@ -19,9 +19,9 @@ impl State { #[notan_main] fn main() -> Result<(), String> { let win = WindowConfig::default() - .lazy_loop(true) - .vsync(true) - .high_dpi(true); + .set_lazy_loop(true) + .set_vsync(true) + .set_high_dpi(true); notan::init_with(State::new) .add_config(win) .add_config(EguiConfig) @@ -58,7 +58,6 @@ fn draw(gfx: &mut Graphics, plugins: &mut Plugins, state: &mut State) { }); output.clear_color(Color::BLACK); - gfx.render(&output); } diff --git a/examples/egui_render_texture.rs b/examples/egui_render_texture.rs index 5dcdd472..ff39e180 100644 --- a/examples/egui_render_texture.rs +++ b/examples/egui_render_texture.rs @@ -6,8 +6,7 @@ use notan::prelude::*; struct State { cube: Cube, render_texture: RenderTexture, - tex_id: egui::TextureId, - img_size: egui::Vec2, + sized_texture: SizedTexture, } impl State { @@ -20,22 +19,22 @@ impl State { .build() .unwrap(); - let img_size = render_texture.size().into(); - let tex_id = gfx.egui_register_texture(&render_texture); + let sized_texture = gfx.egui_register_texture(&render_texture); Self { - img_size, - tex_id, cube, render_texture, + sized_texture, } } } #[notan_main] fn main() -> Result<(), String> { + let win = WindowConfig::default().set_vsync(true).set_high_dpi(true); + notan::init_with(State::new) - .add_config(WindowConfig::new().vsync(true).high_dpi(true)) + .add_config(win) .add_config(EguiConfig) .draw(draw) .build() @@ -47,12 +46,11 @@ fn draw(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins, state: &mut St let mut output = plugins.egui(|ctx| { egui::Window::new("Notan Render Texture").show(ctx, |ui| { - ui.image(state.tex_id, state.img_size); + ui.image(state.sized_texture); }); }); - output.clear_color(Color::BLACK); - // output.needs_repaint is not checked because our render texture needs to be always draw + output.clear_color(Color::BLACK); gfx.render(&output); } @@ -203,7 +201,7 @@ impl Cube { gfx.set_buffer_data(&self.uniform_buffer, &rotated_matrix(self.mvp, self.angle)); let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&ClearOptions { + renderer.begin(Some(ClearOptions { color: Some(Color::new(0.1, 0.2, 0.3, 1.0)), depth: Some(1.0), ..Default::default() diff --git a/examples/egui_shape_widget.rs b/examples/egui_shape_widget.rs index 8255f46a..fe90ac94 100644 --- a/examples/egui_shape_widget.rs +++ b/examples/egui_shape_widget.rs @@ -2,8 +2,8 @@ use notan::draw::*; use notan::egui::{self, *}; use notan::prelude::*; -const WIDTH: i32 = 1200; -const HEIGHT: i32 = 800; +const WIDTH: u32 = 1200; +const HEIGHT: u32 = 800; #[derive(AppState)] struct State { @@ -33,11 +33,11 @@ impl Default for State { #[notan_main] fn main() -> Result<(), String> { let win_config = WindowConfig::new() - .size(WIDTH, HEIGHT) - .multisampling(8) - .lazy_loop(true) - .vsync(true) - .high_dpi(true); + .set_size(WIDTH, HEIGHT) + .set_multisampling(8) + .set_lazy_loop(true) + .set_vsync(true) + .set_high_dpi(true); notan::init_with(State::default) .add_config(win_config) @@ -53,16 +53,14 @@ fn draw(gfx: &mut Graphics, plugins: &mut Plugins, state: &mut State) { draw_egui_widget(ctx, state); }); - if output.needs_repaint() { - // Draw shape - let mut draw = gfx.create_draw(); - draw.clear(state.clear_color); - draw_shape(&mut draw, state); - gfx.render(&draw); + // Draw shape + let mut draw = gfx.create_draw(); + draw.clear(state.clear_color); + draw_shape(&mut draw, state); + gfx.render(&draw); - // Draw the context to the screen or to a RenderTexture - gfx.render(&output); - } + // Draw the context to the screen or to a RenderTexture + gfx.render(&output); } // Draw a Triangle using the properties set on the state @@ -77,8 +75,8 @@ fn draw_shape(draw: &mut Draw, state: &mut State) { let center_y = (a.1 + b.1 + c.1) / 3.0; draw.triangle(a, b, c) - .translate(width * 0.5 - center_x, height * 0.5 - center_y) .rotate_degrees_from((center_x, center_y), state.rotation) + .translate(width * 0.5 - center_x, height * 0.5 - center_y) .skew(state.skew_x, state.skew_y) .color_vertex(state.color.0, state.color.1, state.color.2); } diff --git a/examples/egui_texture.rs b/examples/egui_texture.rs index 94e2fb55..2791892f 100644 --- a/examples/egui_texture.rs +++ b/examples/egui_texture.rs @@ -3,8 +3,7 @@ use notan::prelude::*; #[derive(AppState)] struct State { - tex_id: egui::TextureId, - img_size: egui::Vec2, + sized_texture: egui::SizedTexture, } impl State { @@ -16,16 +15,21 @@ impl State { .build() .unwrap(); - let img_size: egui::Vec2 = texture.size().into(); - let tex_id = gfx.egui_register_texture(&texture); + let sized_texture = gfx.egui_register_texture(&texture); - Self { img_size, tex_id } + Self { sized_texture } } } #[notan_main] fn main() -> Result<(), String> { + let win = WindowConfig::default() + .set_lazy_loop(true) + .set_vsync(true) + .set_high_dpi(true); + notan::init_with(State::new) + .add_config(win) .add_config(EguiConfig) .draw(draw) .build() @@ -34,13 +38,10 @@ fn main() -> Result<(), String> { fn draw(gfx: &mut Graphics, plugins: &mut Plugins, state: &mut State) { let mut output = plugins.egui(|ctx| { egui::Window::new("Notan Texture").show(ctx, |ui| { - ui.image(state.tex_id, state.img_size); + ui.image(state.sized_texture); }); }); output.clear_color(Color::BLACK); - - if output.needs_repaint() { - gfx.render(&output); - } + gfx.render(&output); } diff --git a/examples/game_15_puzzle.rs b/examples/game_15_puzzle.rs index 6ab35132..0b6eb798 100644 --- a/examples/game_15_puzzle.rs +++ b/examples/game_15_puzzle.rs @@ -1,6 +1,7 @@ use notan::draw::*; use notan::prelude::*; use notan::random::rand::prelude::*; +use notan_random::rand; const COLS: usize = 4; const NUMBERS: usize = COLS * COLS; @@ -13,8 +14,8 @@ const OUTLINE_COLOR: Color = Color::from_rgb(0.0, 0.8, 0.7); #[notan_main] fn main() -> Result<(), String> { let win = WindowConfig::default() - .size(BOARD_SIZE as _, BOARD_SIZE as _) - .multisampling(8); + .set_size(BOARD_SIZE as _, BOARD_SIZE as _) + .set_multisampling(8); notan::init_with(State::new) .add_config(win) @@ -172,7 +173,7 @@ impl Board { for _ in 0..1000 { const DIRS: [(i32, i32); 4] = [(0, -1), (-1, 0), (1, 0), (0, 1)]; - let (dx, dy) = DIRS.choose(&mut thread_rng()).unwrap(); + let (dx, dy) = DIRS.choose(&mut rand::rng()).unwrap(); let x_nxt = x + dx; let y_nxt = y + dy; diff --git a/examples/game_of_life.rs b/examples/game_of_life.rs index aae9eac2..b194a843 100644 --- a/examples/game_of_life.rs +++ b/examples/game_of_life.rs @@ -63,7 +63,7 @@ fn main() -> Result<(), String> { let width = WIDTH * 4; let height = HEIGHT * 4; - let win_config = WindowConfig::new().size(width as _, height as _); + let win_config = WindowConfig::new().set_size(width as _, height as _); notan::init_with(setup) .initialize(init) @@ -96,8 +96,8 @@ fn setup(gfx: &mut Graphics) -> State { fn init(state: &mut State) { let mut rng = Random::default(); for _ in 0..500 { - let x = rng.gen_range(0..WIDTH); - let y = rng.gen_range(0..HEIGHT); + let x = rng.random_range(0..WIDTH); + let y = rng.random_range(0..HEIGHT); let neighbors = get_neighbors(x as _, y as _); neighbors.iter().for_each(|(x, y)| { diff --git a/examples/game_pong.rs b/examples/game_pong.rs index 276b45d3..ae244f73 100644 --- a/examples/game_pong.rs +++ b/examples/game_pong.rs @@ -1,8 +1,8 @@ use notan::draw::*; use notan::prelude::*; -const WIDTH: i32 = 800; -const HEIGHT: i32 = 580; +const WIDTH: u32 = 800; +const HEIGHT: u32 = 580; const WALL_SIZE: f32 = 20.0; const PADDLE_WIDTH: f32 = 30.0; const PADDLE_HEIGHT: f32 = PADDLE_WIDTH * 4.0; @@ -15,7 +15,7 @@ const PI: f32 = std::f32::consts::PI; #[notan_main] fn main() -> Result<(), String> { - let win_config = WindowConfig::new().size(WIDTH, HEIGHT).vsync(true); + let win_config = WindowConfig::new().set_size(WIDTH, HEIGHT).set_vsync(true); notan::init_with(State::new) .add_config(win_config) @@ -33,17 +33,17 @@ fn update(app: &mut App, state: &mut State) { } //Move paddle1 with W S - if app.keyboard.is_down(KeyCode::W) { + if app.keyboard.is_down(KeyCode::KeyW) { state.paddle_1.y = (state.paddle_1.y - PADDLE_SPEED * app.timer.delta_f32()).max(WALL_SIZE); - } else if app.keyboard.is_down(KeyCode::S) { + } else if app.keyboard.is_down(KeyCode::KeyS) { state.paddle_1.y = (state.paddle_1.y + PADDLE_SPEED * app.timer.delta_f32()) .min(HEIGHT as f32 - WALL_SIZE - PADDLE_HEIGHT); } //Move paddle2 with arrows UP DOWN - if app.keyboard.is_down(KeyCode::Up) { + if app.keyboard.is_down(KeyCode::ArrowUp) { state.paddle_2.y = (state.paddle_2.y - PADDLE_SPEED * app.timer.delta_f32()).max(WALL_SIZE); - } else if app.keyboard.is_down(KeyCode::Down) { + } else if app.keyboard.is_down(KeyCode::ArrowDown) { state.paddle_2.y = (state.paddle_2.y + PADDLE_SPEED * app.timer.delta_f32()) .min(HEIGHT as f32 - WALL_SIZE - PADDLE_HEIGHT); } @@ -109,7 +109,7 @@ fn draw(gfx: &mut Graphics, state: &mut State) { draw.rect((0.0, 0.0), (width, WALL_SIZE)); draw.rect((0.0, height - WALL_SIZE), (width, WALL_SIZE)); - let points = HEIGHT / WALL_SIZE as i32; + let points = HEIGHT / WALL_SIZE as u32; for i in (0..points).step_by(2) { draw.rect( (width * 0.5 - WALL_SIZE * 0.5, WALL_SIZE * i as f32), @@ -208,7 +208,7 @@ impl BoundCalc for Paddle { } fn random_speed(rng: &mut Random) -> f32 { - rng.gen_range((BALL_SPEED - BALL_SPEED_THRESHOLD)..(BALL_SPEED + BALL_SPEED_THRESHOLD)) + rng.random_range((BALL_SPEED - BALL_SPEED_THRESHOLD)..(BALL_SPEED + BALL_SPEED_THRESHOLD)) } struct Ball { @@ -234,9 +234,9 @@ impl Ball { fn fire(&mut self, rng: &mut Random, left: bool) { self.x = if left { 50.0 } else { WIDTH as f32 - 50.0 }; - self.y = rng.gen_range(WALL_SIZE..(HEIGHT as f32 - WALL_SIZE)); + self.y = rng.random_range(WALL_SIZE..(HEIGHT as f32 - WALL_SIZE)); - let angle_to_fire: f32 = rng.gen_range(0.0..FIRE_ANGLE_MAX); + let angle_to_fire: f32 = rng.random_range(0.0..FIRE_ANGLE_MAX); self.speed_from_angle(random_speed(rng), angle_to_fire, left); } diff --git a/examples/game_snake.rs b/examples/game_snake.rs index b408ea86..eb67b4cb 100644 --- a/examples/game_snake.rs +++ b/examples/game_snake.rs @@ -12,7 +12,7 @@ const MIN_MOVEMENT_MS: f32 = 0.02; #[notan_main] fn main() -> Result<(), String> { - let win_config = WindowConfig::new().size(800, 600).vsync(true); + let win_config = WindowConfig::new().set_size(800, 600).set_vsync(true); notan::init_with(State::new) .add_config(win_config) @@ -213,7 +213,7 @@ impl State { } fn random_xy(rng: &mut Random) -> (usize, usize) { - (rng.gen_range(0..COLS), rng.gen_range(0..ROWS)) + (rng.random_range(0..COLS), rng.random_range(0..ROWS)) } fn xy(index: usize) -> (usize, usize) { @@ -221,10 +221,10 @@ fn xy(index: usize) -> (usize, usize) { } fn change_direction(keyboard: &Keyboard, state: &mut State) { - let up = keyboard.was_pressed(KeyCode::W) || keyboard.was_pressed(KeyCode::Up); - let down = keyboard.was_pressed(KeyCode::S) || keyboard.was_pressed(KeyCode::Down); - let left = keyboard.was_pressed(KeyCode::A) || keyboard.was_pressed(KeyCode::Left); - let right = keyboard.was_pressed(KeyCode::D) || keyboard.was_pressed(KeyCode::Right); + let up = keyboard.was_pressed(KeyCode::KeyW) || keyboard.was_pressed(KeyCode::ArrowUp); + let down = keyboard.was_pressed(KeyCode::KeyS) || keyboard.was_pressed(KeyCode::ArrowDown); + let left = keyboard.was_pressed(KeyCode::KeyA) || keyboard.was_pressed(KeyCode::ArrowLeft); + let right = keyboard.was_pressed(KeyCode::KeyD) || keyboard.was_pressed(KeyCode::ArrowRight); if up && state.dir != Direction::Down { state.dir = Direction::Up; diff --git a/examples/game_tetris.rs b/examples/game_tetris.rs index abdad6ec..8131a31c 100644 --- a/examples/game_tetris.rs +++ b/examples/game_tetris.rs @@ -11,7 +11,9 @@ const ACCELERATION_BY_LINE: f32 = 0.02; #[notan_main] fn main() -> Result<(), String> { - let win_config = WindowConfig::new().size(500, TILE_SIZE * ROWS).vsync(true); + let win_config = WindowConfig::new() + .set_size(500, (TILE_SIZE * ROWS) as _) + .set_vsync(true); notan::init_with(State::new) .add_config(win_config) @@ -24,10 +26,13 @@ fn main() -> Result<(), String> { fn update(app: &mut App, state: &mut State) { state.time += app.timer.delta_f32(); - let down = app.keyboard.was_pressed(KeyCode::Down) || app.keyboard.was_pressed(KeyCode::S); - let up = app.keyboard.was_pressed(KeyCode::Up) || app.keyboard.was_pressed(KeyCode::W); - let left = app.keyboard.was_pressed(KeyCode::Left) || app.keyboard.was_pressed(KeyCode::A); - let right = app.keyboard.was_pressed(KeyCode::Right) || app.keyboard.was_pressed(KeyCode::D); + let down = + app.keyboard.was_pressed(KeyCode::ArrowDown) || app.keyboard.was_pressed(KeyCode::KeyS); + let up = app.keyboard.was_pressed(KeyCode::ArrowUp) || app.keyboard.was_pressed(KeyCode::KeyW); + let left = + app.keyboard.was_pressed(KeyCode::ArrowLeft) || app.keyboard.was_pressed(KeyCode::KeyA); + let right = + app.keyboard.was_pressed(KeyCode::ArrowRight) || app.keyboard.was_pressed(KeyCode::KeyD); if down { state.move_to(MoveTo::Down); @@ -457,7 +462,7 @@ fn index(x: i32, y: i32) -> usize { fn create_texture(gfx: &mut Graphics) -> Texture { let rt = gfx - .create_render_texture(TILE_SIZE, TILE_SIZE) + .create_render_texture(TILE_SIZE as _, TILE_SIZE as _) .build() .unwrap(); diff --git a/examples/game_tic_tac_toe.rs b/examples/game_tic_tac_toe.rs index bd59ffd1..ca76c2b7 100644 --- a/examples/game_tic_tac_toe.rs +++ b/examples/game_tic_tac_toe.rs @@ -41,7 +41,7 @@ impl State { .unwrap(); let mut rng = Random::default(); - let turn = if rng.gen_bool(0.5) { + let turn = if rng.random_bool(0.5) { Player::Cross } else { Player::Circle @@ -57,7 +57,7 @@ impl State { } fn reset(&mut self) { - self.turn = if self.rng.gen_bool(0.5) { + self.turn = if self.rng.random_bool(0.5) { Player::Cross } else { Player::Circle @@ -70,9 +70,9 @@ impl State { fn main() -> Result<(), String> { let win = WindowConfig::default() - .multisampling(8) - .size(WIDTH as _, HEIGHT as _) - .vsync(true); + .set_multisampling(8) + .set_size(WIDTH as _, HEIGHT as _) + .set_vsync(true); notan::init_with(State::new) .add_config(win) diff --git a/examples/input_keyboard.rs b/examples/input_keyboard.rs index da2e2b0e..da9e3cf2 100644 --- a/examples/input_keyboard.rs +++ b/examples/input_keyboard.rs @@ -36,19 +36,19 @@ fn setup(gfx: &mut Graphics) -> State { fn update(app: &mut App, state: &mut State) { state.last_key = app.keyboard.last_key_released(); - if app.keyboard.is_down(KeyCode::W) { + if app.keyboard.is_down(KeyCode::KeyW) { state.y -= MOVE_SPEED * app.timer.delta_f32(); } - if app.keyboard.is_down(KeyCode::A) { + if app.keyboard.is_down(KeyCode::KeyA) { state.x -= MOVE_SPEED * app.timer.delta_f32(); } - if app.keyboard.is_down(KeyCode::S) { + if app.keyboard.is_down(KeyCode::KeyS) { state.y += MOVE_SPEED * app.timer.delta_f32(); } - if app.keyboard.is_down(KeyCode::D) { + if app.keyboard.is_down(KeyCode::KeyD) { state.x += MOVE_SPEED * app.timer.delta_f32(); } } diff --git a/examples/input_keyboard_char.rs b/examples/input_keyboard_char.rs index 4ca9997f..0f538e49 100644 --- a/examples/input_keyboard_char.rs +++ b/examples/input_keyboard_char.rs @@ -31,7 +31,7 @@ fn setup(gfx: &mut Graphics) -> State { fn event(state: &mut State, event: Event) { match event { - Event::ReceivedCharacter(c) if c != '\u{7f}' => { + Event::ReceivedCharacter(c) if c != '\u{7f}' && c != '\u{8}' => { state.msg.push(c); } _ => {} @@ -39,7 +39,7 @@ fn event(state: &mut State, event: Event) { } fn update(app: &mut App, state: &mut State) { - if app.keyboard.was_pressed(KeyCode::Back) && !state.msg.is_empty() { + if app.keyboard.was_pressed(KeyCode::Backspace) && !state.msg.is_empty() { state.msg.pop(); } } diff --git a/examples/input_mouse_local_position.rs b/examples/input_mouse_local_position.rs index 8ff889f2..ca4a8914 100644 --- a/examples/input_mouse_local_position.rs +++ b/examples/input_mouse_local_position.rs @@ -27,8 +27,8 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { let mut rect = draw.rect((0.0, 0.0), size); // set the rectangle's transformation - rect.translate(200.0, 150.0) - .rotate_from((size.0 * 0.5, size.1 * 0.5), state.rot); + rect.rotate_from((size.0 * 0.5, size.1 * 0.5), state.rot) + .translate(200.0, 150.0); // get the local position based on the current projection + matrix let local = rect.screen_to_local_position(app.mouse.x, app.mouse.y); diff --git a/examples/input_mouse_wheel.rs b/examples/input_mouse_wheel.rs index 3ea59d4b..5415197d 100644 --- a/examples/input_mouse_wheel.rs +++ b/examples/input_mouse_wheel.rs @@ -1,4 +1,3 @@ -use notan::app::Event; use notan::draw::*; use notan::prelude::*; @@ -13,7 +12,7 @@ struct State { fn main() -> Result<(), String> { notan::init_with(setup) .add_config(DrawConfig) - .event(event) + .update(update) .draw(draw) .build() } @@ -30,13 +29,13 @@ fn setup(gfx: &mut Graphics) -> State { } } -fn event(state: &mut State, evt: Event) { - match evt { - Event::MouseWheel { delta_x, delta_y } => { - state.x = (state.x + delta_x).max(0.0).min(800.0); - state.y = (state.y + delta_y).max(0.0).min(600.0); - } - _ => {} +fn update(app: &mut App, state: &mut State) { + if app.mouse.is_scrolling() { + let delta_x = app.mouse.wheel_delta.x; + let delta_y = app.mouse.wheel_delta.y; + + state.x = (state.x + delta_x).max(0.0).min(800.0); + state.y = (state.y + delta_y).max(0.0).min(600.0); } } diff --git a/examples/renderer_clear.rs b/examples/renderer_clear.rs index 465cc862..d848b518 100644 --- a/examples/renderer_clear.rs +++ b/examples/renderer_clear.rs @@ -8,8 +8,8 @@ fn main() -> Result<(), String> { fn draw(app: &mut App, gfx: &mut Graphics) { // "Random" color bases on the app's time let color = Color::from_rgb( - app.timer.time_since_init().cos(), - app.timer.time_since_init().sin(), + app.timer.elapsed_f32().cos(), + app.timer.elapsed_f32().sin(), 1.0, ); @@ -17,7 +17,7 @@ fn draw(app: &mut App, gfx: &mut Graphics) { let mut renderer = gfx.create_renderer(); // begin a pass to clear the screen - renderer.begin(Some(&ClearOptions::color(color))); + renderer.begin(Some(ClearOptions::color(color))); renderer.end(); // render to screen diff --git a/examples/renderer_cube.rs b/examples/renderer_cube.rs index 91632999..bd915ede 100644 --- a/examples/renderer_cube.rs +++ b/examples/renderer_cube.rs @@ -162,7 +162,7 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { gfx.set_buffer_data(&state.ubo, &rotated_matrix(state.mvp, state.angle)); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffers(&[&state.vbo, &state.ebo, &state.ubo]); renderer.draw(0, 36); diff --git a/examples/renderer_instancing.rs b/examples/renderer_instancing.rs index cd3a7678..fa2cd8c7 100644 --- a/examples/renderer_instancing.rs +++ b/examples/renderer_instancing.rs @@ -95,7 +95,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { // Renderer pass as usual but instead of .draw uses .draw_instanced let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&ClearOptions::color(Color::BLACK))); + renderer.begin(Some(ClearOptions::color(Color::BLACK))); renderer.set_pipeline(&state.pipeline); renderer.bind_buffers(&[&state.vbo, &state.ubo]); renderer.draw_instanced(0, 3, INSTANCES as _); diff --git a/examples/renderer_instancing_cubes.rs b/examples/renderer_instancing_cubes.rs index 14245039..67b63d00 100644 --- a/examples/renderer_instancing_cubes.rs +++ b/examples/renderer_instancing_cubes.rs @@ -143,12 +143,11 @@ fn setup(gfx: &mut Graphics) -> State { // Generate 1 color per cube let mut rng = Random::default(); let colors = (0..INSTANCES) - .into_iter() .flat_map(|_| { [ - rng.gen_range(0.0..1.0), - rng.gen_range(0.0..1.0), - rng.gen_range(0.0..1.0), + rng.random_range(0.0..1.0), + rng.random_range(0.0..1.0), + rng.random_range(0.0..1.0), 1.0, ] }) @@ -207,7 +206,7 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { gfx.set_buffer_data(&state.ubo, &rotated_matrix(state.mvp, state.angle)); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffers(&[&state.ubo, &state.pos_vbo, &state.color_vbo, &state.ebo]); renderer.draw_instanced(0, 36, INSTANCES as _); diff --git a/examples/renderer_postprocess.rs b/examples/renderer_postprocess.rs index 44706374..8cd31256 100644 --- a/examples/renderer_postprocess.rs +++ b/examples/renderer_postprocess.rs @@ -10,7 +10,7 @@ struct State { #[notan_main] fn main() -> Result<(), String> { notan::init_with(setup) - .add_config(WindowConfig::default().vsync(true)) + .add_config(WindowConfig::default().set_vsync(true)) .draw(draw) .build() } @@ -90,7 +90,7 @@ struct PostProcessTarget { } impl PostProcessTarget { - fn new(gfx: &mut Graphics, width: i32, height: i32) -> Self { + fn new(gfx: &mut Graphics, width: u32, height: u32) -> Self { let render_texture = gfx .create_render_texture(width, height) .with_depth() @@ -165,7 +165,7 @@ impl PostProcessTarget { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&ClearOptions::none())); + renderer.begin(Some(ClearOptions::none())); renderer.set_pipeline(&self.pipeline); renderer.bind_texture(0, &self.render_texture); renderer.bind_buffers(&[ @@ -327,7 +327,7 @@ impl Cube { gfx.set_buffer_data(&self.uniform_buffer, &rotated_matrix(self.mvp, self.angle)); let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&ClearOptions { + renderer.begin(Some(ClearOptions { color: Some(Color::new(0.1, 0.2, 0.3, 1.0)), depth: Some(1.0), ..Default::default() diff --git a/examples/renderer_quad.rs b/examples/renderer_quad.rs index ee67d691..96f035e2 100644 --- a/examples/renderer_quad.rs +++ b/examples/renderer_quad.rs @@ -92,7 +92,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(gfx: &mut Graphics, state: &mut State) { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffers(&[&state.vertex_buffer, &state.index_buffer]); renderer.draw(0, 6); diff --git a/examples/renderer_quad_wireframe.rs b/examples/renderer_quad_wireframe.rs index a9ef0507..42cd1832 100644 --- a/examples/renderer_quad_wireframe.rs +++ b/examples/renderer_quad_wireframe.rs @@ -84,7 +84,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(gfx: &mut Graphics, state: &mut State) { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&ClearOptions::color(Color::BLACK))); + renderer.begin(Some(ClearOptions::color(Color::BLACK))); renderer.set_pipeline(&state.pipeline); renderer.set_primitive(DrawPrimitive::LineStrip); renderer.bind_buffers(&[&state.vertex_buffer, &state.index_buffer]); diff --git a/examples/renderer_render_texture.rs b/examples/renderer_render_texture.rs index f229988f..85e7a5b0 100644 --- a/examples/renderer_render_texture.rs +++ b/examples/renderer_render_texture.rs @@ -70,7 +70,7 @@ fn setup(gfx: &mut Graphics) -> State { .build() .unwrap(); - let (width, height) = (texture.width() as i32, texture.height() as i32); + let (width, height) = (texture.width() as _, texture.height() as _); let render_texture = gfx.create_render_texture(width, height).build().unwrap(); let render_texture2 = gfx.create_render_texture(width, height).build().unwrap(); @@ -154,7 +154,7 @@ fn render_texture( ) -> Renderer { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&ClearOptions { + renderer.begin(Some(ClearOptions { color: clear_color, ..Default::default() })); diff --git a/examples/renderer_stencil.rs b/examples/renderer_stencil.rs new file mode 100644 index 00000000..d99a10ef --- /dev/null +++ b/examples/renderer_stencil.rs @@ -0,0 +1,173 @@ +use notan::prelude::*; + +const VERT: ShaderSource = notan::vertex_shader! { + r#" + #version 450 + layout(location = 0) in vec2 a_pos; + layout(location = 1) in vec3 a_color; + + layout(location = 0) out vec3 v_color; + + void main() { + v_color = a_color; + gl_Position = vec4(a_pos - 0.5, 0.0, 1.0); + } + "# +}; + +const FRAG: ShaderSource = notan::fragment_shader! { + r#" + #version 450 + precision mediump float; + + layout(location = 0) in vec3 v_color; + layout(location = 0) out vec4 color; + + void main() { + color = vec4(v_color, 1.0); + } + "# +}; + +#[derive(AppState)] +struct State { + clear_options: ClearOptions, + mask_pipeline: Pipeline, + pipeline: Pipeline, + mask_vbo: Buffer, + vbo: Buffer, +} + +#[notan_main] +fn main() -> Result<(), String> { + notan::init_with(setup).draw(draw).build() +} + +fn setup(gfx: &mut Graphics) -> State { + let clear_options = ClearOptions { + color: Some(Color::new(0.1, 0.2, 0.3, 1.0)), + depth: None, + stencil: Some(0), + }; + + let vertex_info = VertexInfo::new() + .attr(0, VertexFormat::Float32x2) + .attr(1, VertexFormat::Float32x3); + + let stencil_maks_opts = StencilOptions { + stencil_fail: StencilAction::Keep, + depth_fail: StencilAction::Keep, + pass: StencilAction::Replace, + compare: CompareMode::Always, + read_mask: 0xff, + write_mask: 0xff, + reference: 1, + }; + + let mask_pipeline = gfx + .create_pipeline() + .from(&VERT, &FRAG) + .with_vertex_info(&vertex_info) + .with_stencil(stencil_maks_opts) + .build() + .unwrap(); + + let stencil_opts = StencilOptions { + stencil_fail: StencilAction::Keep, + depth_fail: StencilAction::Keep, + pass: StencilAction::Keep, + compare: CompareMode::Equal, + read_mask: 0xff, + write_mask: 0x00, + reference: 1, + }; + + let pipeline = gfx + .create_pipeline() + .from(&VERT, &FRAG) + .with_vertex_info(&vertex_info) + .with_stencil(stencil_opts) + .build() + .unwrap(); + + // masking vertices + #[rustfmt::skip] + let mask_vertices = [ + 0.5, 1.35, 1.0, 1.0, 1.0, + 0.25, 0.85, 1.0, 1.0, 1.0, + 0.75, 0.85, 1.0, 1.0, 1.0, + + 0.75, 0.85, 1.0, 1.0, 1.0, + 0.5, 0.35, 1.0, 1.0, 1.0, + 1.0, 0.35, 1.0, 1.0, 1.0, + + 0.25, 0.85, 1.0, 1.0, 1.0, + 0.0, 0.35, 1.0, 1.0, 1.0, + 0.5, 0.35, 1.0, 1.0, 1.0, + + 0.5, 0.35, 1.0, 1.0, 1.0, + 0.25, -0.15, 1.0, 1.0, 1.0, + 0.75, -0.15, 1.0, 1.0, 1.0, + + 1.0, 0.35, 1.0, 1.0, 1.0, + 0.75, -0.15, 1.0, 1.0, 1.0, + 1.25, -0.15, 1.0, 1.0, 1.0, + + 0.0, 0.35, 1.0, 1.0, 1.0, + -0.25, -0.15, 1.0, 1.0, 1.0, + 0.25, -0.15, 1.0, 1.0, 1.0, + ]; + + let mask_vbo = gfx + .create_vertex_buffer() + .with_info(&vertex_info) + .with_data(&mask_vertices) + .build() + .unwrap(); + + // triangle + #[rustfmt::skip] + let vertices = [ + 0.5, 1.0, 1.0, 0.2, 0.3, + 0.0, 0.0, 0.1, 1.0, 0.3, + 1.0, 0.0, 0.1, 0.2, 1.0, + ]; + + let vbo = gfx + .create_vertex_buffer() + .with_info(&vertex_info) + .with_data(&vertices) + .build() + .unwrap(); + + State { + clear_options, + mask_pipeline: mask_pipeline, + pipeline: pipeline, + mask_vbo: mask_vbo, + vbo: vbo, + } +} + +fn draw(gfx: &mut Graphics, state: &mut State) { + let mut renderer = gfx.create_renderer(); + + renderer.begin(Some(state.clear_options)); + + // Render the mask + renderer.set_pipeline(&state.mask_pipeline); + renderer.bind_buffer(&state.mask_vbo); + renderer.draw(0, 18); + + renderer.end(); + + // render the triangle + renderer.begin(None); + renderer.set_pipeline(&state.pipeline); + renderer.bind_buffer(&state.vbo); + renderer.draw(0, 3); + + renderer.end(); + + gfx.render(&renderer); +} diff --git a/examples/renderer_texture.rs b/examples/renderer_texture.rs index 25b0287d..2078d64f 100644 --- a/examples/renderer_texture.rs +++ b/examples/renderer_texture.rs @@ -110,7 +110,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(gfx: &mut Graphics, state: &mut State) { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_texture(0, &state.texture); renderer.bind_buffers(&[&state.vertex_buffer, &state.index_buffer]); diff --git a/examples/renderer_texture_r32.rs b/examples/renderer_texture_r32.rs index 5bdf62d4..d029054a 100644 --- a/examples/renderer_texture_r32.rs +++ b/examples/renderer_texture_r32.rs @@ -2,7 +2,7 @@ use notan::math::*; use notan::prelude::*; use std::ops::Rem; -const TEXTURE_SIZE: IVec2 = IVec2::new(10, 10); +const TEXTURE_SIZE: UVec2 = UVec2::new(10, 10); const TOTAL: usize = (TEXTURE_SIZE.x * TEXTURE_SIZE.y) as usize; //language=glsl @@ -55,7 +55,7 @@ struct State { #[notan_main] fn main() -> Result<(), String> { - let win_config = WindowConfig::new().size(400, 400).lazy_loop(true); + let win_config = WindowConfig::new().set_size(400, 400).set_lazy_loop(true); notan::init_with(setup) .add_config(win_config) @@ -131,7 +131,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(gfx: &mut Graphics, state: &mut State) { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_texture(0, &state.texture); renderer.bind_buffers(&[&state.vertex_buffer, &state.index_buffer]); diff --git a/examples/renderer_textured_cube.rs b/examples/renderer_textured_cube.rs index ff966b9c..033b08bf 100644 --- a/examples/renderer_textured_cube.rs +++ b/examples/renderer_textured_cube.rs @@ -166,7 +166,7 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { let vertices_count = 180; let count = vertices_count / state.pipeline.offset(); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffers(&[&state.vertex_buffer, &state.uniform_buffer]); renderer.bind_texture(0, &state.texture); diff --git a/examples/renderer_triangle.rs b/examples/renderer_triangle.rs index da8d6c2e..23fdd92a 100644 --- a/examples/renderer_triangle.rs +++ b/examples/renderer_triangle.rs @@ -81,7 +81,7 @@ fn setup(gfx: &mut Graphics) -> State { fn draw(gfx: &mut Graphics, state: &mut State) { let mut renderer = gfx.create_renderer(); - renderer.begin(Some(&state.clear_options)); + renderer.begin(Some(state.clear_options)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffer(&state.vbo); renderer.draw(0, 3); diff --git a/examples/renderer_uniform_std140.rs b/examples/renderer_uniform_std140.rs index 2a4e36d5..fc811e3b 100644 --- a/examples/renderer_uniform_std140.rs +++ b/examples/renderer_uniform_std140.rs @@ -249,7 +249,7 @@ fn setup(gfx: &mut Graphics) -> State { } fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { - let time = app.timer.time_since_init(); + let time = app.timer.elapsed_f32(); gfx.set_buffer_data(&state.transform_ubo, &Transform::with_view(time)); gfx.set_buffer_data(&state.light_ubo, &Light::with_time(time)); @@ -261,7 +261,7 @@ fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { stencil: None, }; - renderer.begin(Some(&clear)); + renderer.begin(Some(clear)); renderer.set_pipeline(&state.pipeline); renderer.bind_buffers(&[&state.vbo, &state.transform_ubo, &state.light_ubo]); diff --git a/examples/texture_params.rs b/examples/texture_params.rs new file mode 100644 index 00000000..060edc57 --- /dev/null +++ b/examples/texture_params.rs @@ -0,0 +1,231 @@ +// This example shows how to use texture params and mipmaps. + +use notan::draw::*; +use notan::prelude::*; + +const WINDOW_WIDTH: u32 = 1200; +const WINDOW_HEIGHT: u32 = 600; +const CELL_WIDTH: f32 = (WINDOW_WIDTH as f32) / 3.0; +const CELL_HEIGHT: f32 = (WINDOW_HEIGHT as f32) / 2.0; + +fn smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 { + let x = f32::clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + x * x * (3.0 - 2.0 * x) +} + +//language=glsl +const FRAGMENT: ShaderSource = notan::fragment_shader! { + r#" + #version 450 + precision mediump float; + + layout(location = 0) in vec2 v_uvs; + layout(location = 1) in vec4 v_color; + + layout(binding = 0) uniform sampler2D u_texture; + layout(set = 0, binding = 1) uniform TextureInfo { + float lod; + float offset; + float offset_dir; + }; + + layout(location = 0) out vec4 color; + + void main() { + vec2 uv_offset = vec2(1.0, offset_dir) * offset; + color = textureLod(u_texture, v_uvs + uv_offset, lod); + } +"# +}; + +#[derive(AppState)] +struct State { + texture1: Texture, + texture2: Texture, + texture3: Texture, + render_texture1: RenderTexture, + render_texture2: RenderTexture, + render_texture3: RenderTexture, + font: Font, + pipeline: Pipeline, + uniforms: Buffer, + + is_first_render: bool, +} + +#[notan_main] +fn main() -> Result<(), String> { + let window_config = WindowConfig::new().set_size(WINDOW_WIDTH, WINDOW_HEIGHT); + + notan::init_with(init) + .add_config(window_config) + .add_config(DrawConfig) + .draw(draw) + .build() +} + +fn init(gfx: &mut Graphics) -> State { + let font = gfx + .create_font(include_bytes!("assets/Ubuntu-B.ttf")) + .unwrap(); + + let ferris = include_bytes!("assets/ferris.png"); + + // Texture + let texture1 = gfx + .create_texture() + .from_image(ferris) + .with_premultiplied_alpha() + .build() + .unwrap(); + + // Texture w/ mipmap + let texture2 = gfx + .create_texture() + .from_image(ferris) + .with_premultiplied_alpha() + .with_filter(TextureFilter::Linear, TextureFilter::Linear) + .with_mipmaps(true) + .build() + .unwrap(); + + // Texture w/ mipmap & wrap + let texture3 = gfx + .create_texture() + .from_image(ferris) + .with_premultiplied_alpha() + .with_wrap(TextureWrap::Repeat, TextureWrap::Repeat) + .with_filter(TextureFilter::Linear, TextureFilter::Linear) + .with_mipmaps(true) + .build() + .unwrap(); + + let (width, height) = texture1.size(); + + // RenderTexture + let render_texture1 = gfx + .create_render_texture(width as u32, height as u32) + .with_format(TextureFormat::Rgba32) + .build() + .unwrap(); + + // RenderTexture w/ mipmap + let render_texture2 = gfx + .create_render_texture(width as u32, height as u32) + .with_format(TextureFormat::Rgba32) + .with_mipmaps(true) + .with_filter(TextureFilter::Linear, TextureFilter::Linear) + .build() + .unwrap(); + + // RenderTexture w/ mipmap & wrap + let render_texture3 = gfx + .create_render_texture(width as u32, height as u32) + .with_format(TextureFormat::Rgba32) + .with_mipmaps(true) + .with_filter(TextureFilter::Linear, TextureFilter::Linear) + .with_wrap(TextureWrap::Repeat, TextureWrap::Repeat) + .build() + .unwrap(); + + let pipeline = create_image_pipeline(gfx, Some(&FRAGMENT)).unwrap(); + let uniforms = gfx + .create_uniform_buffer(1, "TextureInfo") + .with_data(&[0.0]) + .build() + .unwrap(); + + State { + font, + + texture1, + texture2, + texture3, + + render_texture1, + render_texture2, + render_texture3, + + pipeline, + uniforms, + + is_first_render: true, + } +} + +fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { + if state.is_first_render { + // Copy the original texture content to the RenderTextures + for rt in [ + &state.render_texture1, + &state.render_texture2, + &state.render_texture3, + ] { + let mut draw = gfx.create_draw(); + draw.set_size(state.texture1.width(), state.texture1.height()); + draw.image(&state.texture1).blend_mode(BlendMode::NONE); + gfx.render_to(rt, &draw); + } + + state.is_first_render = false; + } + + // Update params + let time = app.timer.elapsed_f32(); + let lod = smoothstep(0.0, 1.0, f32::max(time.cos(), 0.0)) * 5.0; + let offset = smoothstep(0.0, 1.0, f32::max(-time.cos(), 0.0)) * 0.5; + + // Clear canvas + let mut draw = gfx.create_draw(); + draw.clear(Color::GRAY); + gfx.render(&draw); + + let cells = [ + (&state.texture1, "Texture"), + (&state.texture2, "Texture w/ mipmap"), + (&state.texture3, "Texture w/ mipmap & wrap"), + (&state.render_texture1, "RenderTexture"), + (&state.render_texture2, "RenderTexture w/ mipmap"), + (&state.render_texture3, "RenderTexture w/ mipmap & wrap"), + ]; + let scale = CELL_WIDTH / state.texture1.width(); + + // Render textures + for (i, (tex, label)) in cells.iter().enumerate() { + let x = (i % 3) as f32; + let y = (i / 3) as f32; + + let offset_dir = if tex.is_render_texture() { -1.0 } else { 1.0 }; + gfx.set_buffer_data(&state.uniforms, &[lod, offset, offset_dir]); + + let mut draw = gfx.create_draw(); + + draw.image_pipeline() + .pipeline(&state.pipeline) + .uniform_buffer(&state.uniforms); + + draw.image(tex) + .blend_mode(BlendMode::OVER) + .scale(scale, scale) + .translate(x * CELL_WIDTH, y * CELL_HEIGHT + 10.0); + + draw.text(&state.font, label) + .size(20.0) + .position(x * CELL_WIDTH + 10.0, y * CELL_HEIGHT + 10.0) + .color(Color::WHITE); + + gfx.render(&draw); + } + + // Show LOD and offset as text + let mut draw = gfx.create_draw(); + draw.text( + &state.font, + &format!("LOD: {:.2} / Offset: {:.2}", lod, offset), + ) + .size(20.0) + .position(10.0, WINDOW_HEIGHT as f32 - 30.0) + .color(Color::WHITE); + + gfx.render(&draw); +} diff --git a/examples/texture_to_file.rs b/examples/texture_to_file.rs index ac732b49..c0c6e842 100644 --- a/examples/texture_to_file.rs +++ b/examples/texture_to_file.rs @@ -117,7 +117,7 @@ impl State { #[notan_main] fn main() -> Result<(), String> { - let win_config = WindowConfig::default().vsync(true).lazy_loop(true); + let win_config = WindowConfig::default().set_vsync(true).set_lazy_loop(true); notan::init_with(State::new) .add_config(win_config) diff --git a/examples/window_config.rs b/examples/window_config.rs index fbe3d641..dbeb9e0e 100644 --- a/examples/window_config.rs +++ b/examples/window_config.rs @@ -6,13 +6,13 @@ use notan::prelude::*; fn main() -> Result<(), String> { // Check the documentation for more options let window_config = WindowConfig::new() - .title("Window Config Demo") - .size(1026, 600) // window's size - .vsync(true) // enable vsync - .resizable(true) // window can be resized - .min_size(600, 400) // Set a minimum window size - .window_icon(Some(PathBuf::from("./examples/assets/rust.ico"))) - .taskbar_icon(Some(PathBuf::from("./examples/assets/rust.ico"))); + .set_title("Window Config Demo") + .set_size(1026, 600) // window's size + .set_vsync(true) // enable vsync + .set_resizable(true) // window can be resized + .set_min_size(600, 400) // Set a minimum window size + .set_window_icon(Some(PathBuf::from("./examples/assets/rust.ico"))) + .set_taskbar_icon(Some(PathBuf::from("./examples/assets/rust.ico"))); notan::init().add_config(window_config).build() } diff --git a/examples/window_focus.rs b/examples/window_focus.rs new file mode 100644 index 00000000..87df8e9a --- /dev/null +++ b/examples/window_focus.rs @@ -0,0 +1,44 @@ +use notan::draw::*; +use notan::prelude::*; + +#[derive(AppState)] +struct State { + font: Font, +} + +#[notan_main] +fn main() -> Result<(), String> { + notan::init_with(setup) + .add_config(DrawConfig) + .draw(draw) + .build() +} + +fn setup(gfx: &mut Graphics) -> State { + let font = gfx + .create_font(include_bytes!("assets/Ubuntu-B.ttf")) + .unwrap(); + State { font } +} + +fn draw(app: &mut App, gfx: &mut Graphics, state: &mut State) { + let window = app.window(); + let ww = window.width() as f32; + let hh = window.height() as f32; + + let text = if window.is_focused() { + "Window is currently focused" + } else { + "Window is no longer focused" + }; + + let mut draw = gfx.create_draw(); + draw.clear(Color::BLACK); + draw.text(&state.font, text) + .position(ww * 0.5, hh * 0.5) + .size(40.0) + .h_align_center() + .v_align_middle(); + + gfx.render(&draw); +} diff --git a/examples/window_icon_from_raw.rs b/examples/window_icon_from_raw.rs index 0d36e021..6966065b 100644 --- a/examples/window_icon_from_raw.rs +++ b/examples/window_icon_from_raw.rs @@ -4,7 +4,7 @@ use notan::prelude::*; fn main() -> Result<(), String> { // Check the documentation for more options let window_config = WindowConfig::new() - .title("Window Icon Data Demo") + .set_title("Window Icon Data Demo") .set_window_icon_data(Some(include_bytes!("./assets/rust.ico"))) .set_taskbar_icon_data(Some(include_bytes!("./assets/rust.ico"))); diff --git a/examples/window_initial_position.rs b/examples/window_initial_position.rs new file mode 100644 index 00000000..0244e0cc --- /dev/null +++ b/examples/window_initial_position.rs @@ -0,0 +1,7 @@ +use notan::prelude::*; + +#[notan_main] +fn main() -> Result<(), String> { + let win = WindowConfig::default().set_position(100, 100); + notan::init().add_config(win).build() +} diff --git a/examples/window_transparent.rs b/examples/window_transparent.rs index 75e35a90..e27c85e2 100644 --- a/examples/window_transparent.rs +++ b/examples/window_transparent.rs @@ -3,7 +3,9 @@ use notan::prelude::*; #[notan_main] fn main() -> Result<(), String> { - let win = WindowConfig::default().transparent(true).decorations(false); + let win = WindowConfig::default() + .set_transparent(true) + .set_decorations(false); notan::init() .add_config(win) .add_config(DrawConfig) // Simple way to add the draw extension diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh deleted file mode 100755 index 0f362426..00000000 --- a/scripts/build_docs.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cargo doc --all-features - diff --git a/scripts/build_examples.sh b/scripts/build_examples.sh deleted file mode 100755 index 1a9102e4..00000000 --- a/scripts/build_examples.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -mkdir -p ./docs/examples/assets -cp -R ./examples/assets ./docs/examples - -doc_body="
    \n" - -compile() { - f=$1 - f=${f/\.\/examples\//""} - f=${f/.rs/""} - ./scripts/web_example.sh $f --release --no-assets - - url="examples/${f}.html" - image="examples/images/${f}.jpg" - doc_body="${doc_body}\n
  • \"${f}\"
    ${f}
  • " -} - -for f in ./examples/*.rs; do - compile "$f" -done - -wait - -doc_body="${doc_body}\n
" -cp ./scripts/docs.html ./docs/index.html -index=$(sed "s#{{ BODY }}#${doc_body}#g" "./scripts/docs.html") -echo "${index}" > ./docs/index.html diff --git a/scripts/build_web_examples.sh b/scripts/build_web_examples.sh new file mode 100755 index 00000000..ed6ae5b3 --- /dev/null +++ b/scripts/build_web_examples.sh @@ -0,0 +1 @@ +cargo xtask examples web --release \ No newline at end of file diff --git a/scripts/publish.sh b/scripts/publish.sh index 01e71b56..5d5185b7 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -3,11 +3,11 @@ # if crate A depends on crate B, B must come before A in this list crates=( notan_core + notan_math notan_input notan_audio notan_random notan_utils - notan_math notan_macro notan_graphics notan_app diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh new file mode 100755 index 00000000..be92b40b --- /dev/null +++ b/scripts/upgrade.sh @@ -0,0 +1,13 @@ +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +for crate in crates/*; do + current="$SCRIPT_DIR/../$crate" + echo "$current" + cd $current + cargo upgrade +done + +current="$SCRIPT_DIR/../" +echo $current +cd $current +cargo upgrade diff --git a/scripts/web_example.sh b/scripts/web_example.sh deleted file mode 100755 index 5e880618..00000000 --- a/scripts/web_example.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -mkdir -p ./docs/examples/$1 - -# web_sys_unstable_apis is required to enable the web_sys clipboard API -# https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html -export RUSTFLAGS=--cfg=web_sys_unstable_apis -features=glyph,egui,text,extra,audio,links,drop_files,clipboard,save_file,texture_to_file - -if [[ $2 == '--release' ]]; -then - cargo build --target wasm32-unknown-unknown --release --example $1 --features=$features - wasm-bindgen ./target/wasm32-unknown-unknown/release/examples/$1.wasm --out-dir ./docs/examples/$1 --no-modules --browser - wasm-opt -O -o ./docs/examples/$1/$1_bg.wasm ./docs/examples/$1/$1_bg.wasm - - if [[ $3 == '--gzip' ]]; - then - gzip -f ./docs/examples/$1/$1_bg.wasm - gzip -f ./docs/examples/$1/$1.js - fi - -else - cargo build --target wasm32-unknown-unknown --example $1 --features=$features - wasm-bindgen ./target/wasm32-unknown-unknown/debug/examples/$1.wasm --out-dir ./docs/examples/$1 --no-modules --browser --keep-debug --debug -fi - -cp ./scripts/example.html ./docs/examples/$1.html -index=$(sed "s/{{ EXAMPLE }}/${1}/g" "./scripts/example.html") -echo "${index}" > ./docs/examples/$1.html - -if [[ $3 != '--no-assets' ]]; -then - cp -R ./examples/assets ./docs/examples -fi diff --git a/scripts/web_keyboard.py b/scripts/web_keyboard.py new file mode 100644 index 00000000..c5d9b170 --- /dev/null +++ b/scripts/web_keyboard.py @@ -0,0 +1,14 @@ + +if __name__ == '__main__': + import re + import requests + from sortedcontainers import SortedSet + regex = re.compile(r'\s*"([0-9A-Za-z]+)"') + codes = SortedSet() + data = requests.get('https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values').text + for m in regex.finditer(data): + code = m.groups()[0] + if code != '' and code != 'Unidentified': + codes.add(code) + for code in codes: + print(f' "{code}" => KeyCode::{code},') diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..0cab6a08 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "xtask" +version = "0.1.0" +publish = false +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true +readme = "README.md" +description = "" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +flate2 = "1.0" +fs_extra = "1.3.0" +xflags = "0.3.2" diff --git a/xtask/README.md b/xtask/README.md new file mode 100644 index 00000000..09cb263a --- /dev/null +++ b/xtask/README.md @@ -0,0 +1,4 @@ +xtask +=== + +Adds useful development commands using xtask. Use the command `cargo xtask` to show the help. diff --git a/scripts/docs.html b/xtask/res/docs.html similarity index 100% rename from scripts/docs.html rename to xtask/res/docs.html diff --git a/scripts/example.html b/xtask/res/example.html similarity index 91% rename from scripts/example.html rename to xtask/res/example.html index 45851129..af19782c 100644 --- a/scripts/example.html +++ b/xtask/res/example.html @@ -22,8 +22,8 @@ div#container { width: 100%; display: flex; - align-items: center; - justify-content: center; + align-items: safe center; + justify-content: safe center; } div#source-code { @@ -53,7 +53,7 @@

Source Code
- +
- \ No newline at end of file + diff --git a/xtask/src/cli.rs b/xtask/src/cli.rs new file mode 100644 index 00000000..e95c17c5 --- /dev/null +++ b/xtask/src/cli.rs @@ -0,0 +1,98 @@ +#![allow(unreachable_pub)] + +use std::str::FromStr; + +xflags::xflags! { + src "./src/cli.rs" + + cmd cli { + cmd docs {} + cmd example { + required name: String + required target: TargetType + + optional --release + optional --no-assets + optional --gzip + } + + cmd examples { + required target: TargetType + + optional --release + optional --gzip + } + } +} + +// generated start +// The following code is generated by `xflags` macro. +// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. +#[derive(Debug)] +pub struct Cli { + pub subcommand: CliCmd, +} + +#[derive(Debug)] +pub enum CliCmd { + Docs(Docs), + Example(Example), + Examples(Examples), +} + +#[derive(Debug)] +pub struct Docs; + +#[derive(Debug)] +pub struct Example { + pub name: String, + pub target: TargetType, + + pub release: bool, + pub no_assets: bool, + pub gzip: bool, +} + +#[derive(Debug)] +pub struct Examples { + pub target: TargetType, + + pub release: bool, + pub gzip: bool, +} + +impl Cli { + #[allow(dead_code)] + pub fn from_env_or_exit() -> Self { + Self::from_env_or_exit_() + } + + #[allow(dead_code)] + pub fn from_env() -> xflags::Result { + Self::from_env_() + } + + #[allow(dead_code)] + pub fn from_vec(args: Vec) -> xflags::Result { + Self::from_vec_(args) + } +} +// generated end + +#[derive(Copy, Clone, Debug, Default)] +pub enum TargetType { + #[default] + Msvc, + Web, +} + +impl FromStr for TargetType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "msvc" => Ok(Self::Msvc), + "web" => Ok(Self::Web), + _ => Err("Invalid option".to_owned()), + } + } +} diff --git a/xtask/src/cli_docs.rs b/xtask/src/cli_docs.rs new file mode 100644 index 00000000..e36b282a --- /dev/null +++ b/xtask/src/cli_docs.rs @@ -0,0 +1,40 @@ +use std::path::PathBuf; +use std::process::Command; +use std::{env, fs}; + +use crate::cli::Docs; +use crate::{project_root, DynError}; + +impl Docs { + pub(crate) fn run(self) -> Result<(), DynError> { + docs_clean()?; + docs_run()?; + + Ok(()) + } +} + +fn docs_clean() -> Result<(), DynError> { + let _ = fs::remove_dir_all(dist_doc_dir()); + fs::create_dir_all(dist_doc_dir())?; + + Ok(()) +} + +fn docs_run() -> Result<(), DynError> { + let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + let status = Command::new(cargo) + .current_dir(project_root()) + .args(["doc", "--all-features"]) + .status()?; + + if !status.success() { + Err("Command 'cargo doc --all-features' failed")?; + } + + Ok(()) +} + +fn dist_doc_dir() -> PathBuf { + project_root().join("target/doc") +} diff --git a/xtask/src/cli_example.rs b/xtask/src/cli_example.rs new file mode 100644 index 00000000..68102af7 --- /dev/null +++ b/xtask/src/cli_example.rs @@ -0,0 +1,13 @@ +use crate::cli::{Example, TargetType}; +use crate::DynError; + +impl Example { + pub(crate) fn run(self) -> Result<(), DynError> { + match self.target { + TargetType::Msvc => self.run_msvc()?, + TargetType::Web => self.run_web()?, + } + + Ok(()) + } +} diff --git a/xtask/src/cli_example_msvc.rs b/xtask/src/cli_example_msvc.rs new file mode 100644 index 00000000..8df06234 --- /dev/null +++ b/xtask/src/cli_example_msvc.rs @@ -0,0 +1,52 @@ +use std::fs; +use std::path::PathBuf; + +use crate::cli::{Example, TargetType}; +use crate::{cargo_build, copy_assets, project_root, DynError}; + +impl Example { + pub(crate) fn run_msvc(self) -> Result<(), DynError> { + let status = match self.release { + true => cargo_build(TargetType::Msvc, "release", self.name.as_str())?, + false => cargo_build(TargetType::Msvc, "dev", self.name.as_str())?, + }; + + if !status.success() { + Err("Command 'cargo build' failed")?; + } + + let name_str = self.name.as_str(); + let executable = format!("{name_str}.exe"); + + fs::create_dir_all(docs_msvc_dir(self.release))?; + let _ = fs::copy( + dist_msvc_dir(self.release).join(executable.as_str()), + docs_msvc_dir(self.release).join(executable.as_str()), + ); + + if !self.no_assets { + copy_assets(docs_msvc_dir(self.release)); + } + + Ok(()) + } +} + +pub fn docs_msvc_dir(release: bool) -> PathBuf { + project_root() + .join("docs/msvc_examples/") + .join(match release { + true => "release", + false => "debug", + }) +} + +fn dist_msvc_dir(release: bool) -> PathBuf { + project_root() + .join("target/x86_64-pc-windows-msvc") + .join(match release { + true => "release", + false => "debug", + }) + .join("examples") +} diff --git a/xtask/src/cli_example_web.rs b/xtask/src/cli_example_web.rs new file mode 100644 index 00000000..4064f491 --- /dev/null +++ b/xtask/src/cli_example_web.rs @@ -0,0 +1,109 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Write}; +use std::path::PathBuf; + +use crate::cli::{Example, TargetType}; +use crate::{cargo_build, copy_assets, gz_file, project_root, wasm_bindgen, wasm_opt, DynError}; + +impl Example { + pub(crate) fn run_web(self) -> Result<(), DynError> { + let status = match self.release { + true => cargo_build(TargetType::Web, "release", self.name.as_str())?, + false => cargo_build(TargetType::Web, "dev", self.name.as_str())?, + }; + + if !status.success() { + Err("Command 'cargo build' failed")?; + } + + let name_str = self.name.as_str(); + let wasm = format!("{name_str}.wasm"); + + let input = dist_web_dir(self.release) + .join(wasm.as_str()) + .to_string_lossy() + .into_owned(); + let output = docs_web_dir(name_str).to_string_lossy().into_owned(); + + let status = wasm_bindgen(input.as_str(), output.as_str(), !self.release)?; + if !status.success() { + Err("Command 'wasm-bindgen' failed")?; + } + + if self.release { + let wasm_file = format!("{name_str}_bg.wasm"); + let inout = docs_web_dir(name_str) + .join(wasm_file.as_str()) + .to_string_lossy() + .into_owned(); + + let status = wasm_opt(inout.as_str(), inout.as_str())?; + if !status.success() { + Err("Command 'wasm-opt' failed")?; + } + + if self.gzip { + let wasm_gz = format!("{wasm_file}.gz"); + let wasm_gz_output = docs_web_dir(name_str) + .join(wasm_gz) + .to_string_lossy() + .into_owned(); + + let js_gz_input = docs_web_dir(name_str) + .join(format!("{name_str}.js")) + .to_string_lossy() + .into_owned(); + let js_gz_output = docs_web_dir(name_str) + .join(format!("{name_str}.js.gz")) + .to_string_lossy() + .into_owned(); + + gz_file(inout.as_str(), wasm_gz_output.as_str()); + gz_file(js_gz_input.as_str(), js_gz_output.as_str()); + } + } + + let example_file = format!("{name_str}.html"); + + let file_in = File::open(res_dir().join("example.html"))?; + let reader = BufReader::new(file_in); + + let mut output_lines = Vec::new(); + for line in reader.lines() { + let mut line = line?; + + line = line.replace("{{ EXAMPLE }}", self.name.as_str()); + output_lines.push(line); + } + + let mut file_out = File::create(docs_web_dir("").join(example_file.as_str()))?; + + for line in &output_lines { + writeln!(file_out, "{}", line)?; + } + + if !self.no_assets { + copy_assets(docs_web_dir("")) + } + + Ok(()) + } +} + +pub fn docs_web_dir(name: &str) -> PathBuf { + project_root().join("docs").join("examples").join(name) +} + +fn dist_web_dir(release: bool) -> PathBuf { + project_root() + .join("target/wasm32-unknown-unknown") + .join(match release { + true => "release", + false => "debug", + }) + .join("examples") +} + +pub fn res_dir() -> PathBuf { + project_root().join("xtask/res") +} diff --git a/xtask/src/cli_examples.rs b/xtask/src/cli_examples.rs new file mode 100644 index 00000000..4c1893de --- /dev/null +++ b/xtask/src/cli_examples.rs @@ -0,0 +1,41 @@ +use std::fs; +use std::path::PathBuf; + +use crate::cli::{Examples, TargetType}; +use crate::DynError; + +impl Examples { + pub(crate) fn run(self) -> Result<(), DynError> { + match self.target { + TargetType::Msvc => self.run_msvc()?, + TargetType::Web => self.run_web()?, + } + + Ok(()) + } + + pub(crate) fn list_files(self, path: &str) -> Result, DynError> { + let entries = fs::read_dir(path)?; + let dir_entries: Vec<_> = entries + .filter_map(Result::ok) + .filter_map(|entry| { + let metadata = entry.metadata().ok()?; + let is_file = metadata.is_file(); + let is_hidden = entry + .file_name() + .to_str() + .map(|name| name.starts_with('.')) + .unwrap_or_default(); + let is_valid = is_file && !is_hidden; + if is_valid { + Some(entry) + } else { + None + } + }) + .map(|entry| entry.path()) + .collect(); + + Ok(dir_entries.into_iter()) + } +} diff --git a/xtask/src/cli_examples_msvc.rs b/xtask/src/cli_examples_msvc.rs new file mode 100644 index 00000000..44629cf0 --- /dev/null +++ b/xtask/src/cli_examples_msvc.rs @@ -0,0 +1,35 @@ +use crate::cli::{Example, Examples}; +use crate::cli_example_msvc::docs_msvc_dir; +use crate::{copy_assets, project_root, DynError}; + +impl Examples { + pub(crate) fn run_msvc(self) -> Result<(), DynError> { + copy_assets(docs_msvc_dir(self.release).join("assets")); + + let examples_path = project_root() + .join("examples") + .to_string_lossy() + .into_owned(); + + let target = self.target; + let release = self.release; + let gzip = self.gzip; + + self.list_files(examples_path.as_str())? + .for_each(|example| { + let name = example.file_stem().unwrap().to_str().unwrap().to_owned(); + // eprintln!("{}", name); + let example = Example { + name, + target, + release, + no_assets: true, + gzip, + }; + + let _ = example.run(); + }); + + Ok(()) + } +} diff --git a/xtask/src/cli_examples_web.rs b/xtask/src/cli_examples_web.rs new file mode 100644 index 00000000..e43b0ddc --- /dev/null +++ b/xtask/src/cli_examples_web.rs @@ -0,0 +1,66 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Write}; + +use crate::cli::{Example, Examples}; +use crate::cli_example_web::docs_web_dir; +use crate::{copy_assets, project_root, DynError}; + +impl Examples { + pub(crate) fn run_web(self) -> Result<(), DynError> { + copy_assets(docs_web_dir("assets")); + + let mut doc_body = String::from("
    \n"); + + let examples_path = project_root() + .join("examples") + .to_string_lossy() + .into_owned(); + + let target = self.target; + let release = self.release; + let gzip = self.gzip; + + self.list_files(examples_path.as_str())? + .for_each(|example| { + let name = example.file_stem().unwrap().to_str().unwrap().to_owned(); + let name_str = name.as_str().to_owned(); + + let example = Example { + name, + target, + release, + no_assets: true, + gzip, + }; + + let _ = example.run(); + + let url = format!("examples/{name_str}.html"); + let image = format!("examples/images/{name_str}.jpg"); + let tmp = format!("\n
  • \"{name_str}\"
    {name_str}
  • "); + + doc_body.push_str(tmp.as_str()); + }); + + doc_body.push_str("\n
"); + + let file_in = File::open(crate::cli_example_web::res_dir().join("docs.html"))?; + let reader = BufReader::new(file_in); + + let mut output_lines = Vec::new(); + for line in reader.lines() { + let mut line = line?; + + line = line.replace("{{ BODY }}", doc_body.as_str()); + output_lines.push(line); + } + + let mut file_out = File::create(docs_web_dir("../").join("index.html"))?; + + for line in &output_lines { + writeln!(file_out, "{}", line)?; + } + + Ok(()) + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..8788cad6 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,133 @@ +mod cli; + +mod cli_docs; +mod cli_example; +mod cli_example_msvc; +mod cli_example_web; +mod cli_examples; +mod cli_examples_msvc; +mod cli_examples_web; + +use crate::cli::TargetType; +use cli::{Cli, CliCmd}; +use flate2::write::GzEncoder; +use flate2::Compression; +use std::env; +use std::fs::File; +use std::io::{copy, BufReader, Error}; +use std::path::PathBuf; +use std::process::{Command, ExitStatus}; + +type DynError = Box; + +fn main() { + if let Err(e) = try_main() { + eprintln!("{}", e); + std::process::exit(-1); + } +} + +fn try_main() -> Result<(), DynError> { + let flags = Cli::from_env_or_exit(); + + match flags.subcommand { + CliCmd::Docs(cmd) => cmd.run()?, + CliCmd::Example(cmd) => cmd.run()?, + CliCmd::Examples(cmd) => cmd.run()?, + } + + Ok(()) +} + +fn project_root() -> PathBuf { + let dir = + env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()); + + PathBuf::from(dir).parent().unwrap().to_owned() +} + +fn assets_dir() -> PathBuf { + project_root().join("examples/assets") +} + +fn copy_assets(to: PathBuf) { + let options = fs_extra::dir::CopyOptions::new() + .overwrite(true) + .copy_inside(true); + + let paths = vec![assets_dir().as_path().to_owned()]; + let _ = fs_extra::copy_items(&paths, to, &options); +} + +fn cargo_build(target: TargetType, profile: &str, name: &str) -> Result { + let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + + // We want this to be configurable? + let features = + "glyph,egui,text,extra,audio,links,drop_files,clipboard,save_file,texture_to_file"; + let features_arg = format!("--features={features}"); + + match target { + TargetType::Msvc => Command::new(cargo) + .current_dir(project_root()) + .args([ + "build", + "--target", + "x86_64-pc-windows-msvc", + "--profile", + profile, + "--example", + name, + features_arg.as_str(), + ]) + .status(), + + TargetType::Web => Command::new(cargo) + .current_dir(project_root()) + .env("RUSTFLAGS", "--cfg=web_sys_unstable_apis") + .args([ + "build", + "--target", + "wasm32-unknown-unknown", + "--profile", + profile, + "--example", + name, + features_arg.as_str(), + ]) + .status(), + } +} + +fn wasm_bindgen(input: &str, output: &str, debug: bool) -> Result { + Command::new("wasm-bindgen") + .current_dir(project_root()) + .args( + [ + [input, "--out-dir", output, "--no-modules"].as_slice(), + match debug { + true => ["--keep-debug", "--debug"].as_slice(), + false => [].as_slice(), + }, + ] + .concat(), + ) + .status() +} + +fn wasm_opt(input: &str, output: &str) -> Result { + Command::new("wasm-opt") + .current_dir(project_root()) + .args(["-O", "-o", input, output]) + .status() +} + +fn gz_file(input: &str, output: &str) { + let mut input_wasm = BufReader::new(File::open(input).unwrap()); + let output_wasm = File::create(output).unwrap(); + + let mut encoder_wasm = GzEncoder::new(output_wasm, Compression::default()); + + copy(&mut input_wasm, &mut encoder_wasm).unwrap(); + encoder_wasm.finish().unwrap(); +}