diff --git a/.github/workflows/build-all-features.yml b/.github/workflows/build-all-features.yml index dc367a5..da46711 100644 --- a/.github/workflows/build-all-features.yml +++ b/.github/workflows/build-all-features.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Build run: cargo build --verbose --all-features - name: Run tests diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml index b567f98..49d0683 100644 --- a/.github/workflows/build-appimage.yml +++ b/.github/workflows/build-appimage.yml @@ -15,19 +15,22 @@ env: jobs: build_appimage: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v3 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Cargo Build run: | - .github/workflows/scripts/appimage_build_wlx.sh + ../.github/workflows/scripts/appimage_build_wlx.sh - name: Package AppImage run: | - .github/workflows/scripts/appimage_package.sh + ../.github/workflows/scripts/appimage_package.sh - name: Upload AppImage uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build-default.yml b/.github/workflows/build-default.yml index 614b066..85665da 100644 --- a/.github/workflows/build-default.yml +++ b/.github/workflows/build-default.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Run cargo fmt run: cargo fmt --check - name: Build diff --git a/.github/workflows/build-full-appimage.yml b/.github/workflows/build-full-appimage.yml index ad9df73..d4df61c 100644 --- a/.github/workflows/build-full-appimage.yml +++ b/.github/workflows/build-full-appimage.yml @@ -15,22 +15,25 @@ env: jobs: build_appimage: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v3 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Cargo Build run: | - .github/workflows/scripts/appimage_build_wlx.sh + ../.github/workflows/scripts/appimage_build_wlx.sh - name: Build WayVR Dashboard run: | - .github/workflows/scripts/appimage_build_wayvr_dashboard.sh + ../.github/workflows/scripts/appimage_build_wayvr_dashboard.sh - name: Package AppImage run: | - .github/workflows/scripts/appimage_package_full.sh + ../.github/workflows/scripts/appimage_package_full.sh - name: Upload AppImage uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build-wayland-openvr.yml b/.github/workflows/build-wayland-openvr.yml index dce2e71..07289dc 100644 --- a/.github/workflows/build-wayland-openvr.yml +++ b/.github/workflows/build-wayland-openvr.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Build run: cargo build --verbose --no-default-features --features=wayland,openvr - name: Run tests diff --git a/.github/workflows/build-wayland-openxr-openvr-wayvr.yml b/.github/workflows/build-wayland-openxr-openvr-wayvr.yml index 1bc3589..cd37e97 100644 --- a/.github/workflows/build-wayland-openxr-openvr-wayvr.yml +++ b/.github/workflows/build-wayland-openxr-openvr-wayvr.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Build run: cargo build --verbose --no-default-features --features=wayland,openxr,openvr,wayvr - name: Run tests diff --git a/.github/workflows/build-wayland-openxr.yml b/.github/workflows/build-wayland-openxr.yml index 1e64b24..4b433f0 100644 --- a/.github/workflows/build-wayland-openxr.yml +++ b/.github/workflows/build-wayland-openxr.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Build run: cargo build --verbose --no-default-features --features=wayland,openxr - name: Run tests diff --git a/.github/workflows/build-x11-openvr.yml b/.github/workflows/build-x11-openvr.yml index 86327a1..8b3ece3 100644 --- a/.github/workflows/build-x11-openvr.yml +++ b/.github/workflows/build-x11-openvr.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Build run: cargo build --verbose --no-default-features --features=x11,openvr - name: Run tests diff --git a/.github/workflows/build-x11-openxr.yml b/.github/workflows/build-x11-openxr.yml index be7f3d6..30082ab 100644 --- a/.github/workflows/build-x11-openxr.yml +++ b/.github/workflows/build-x11-openxr.yml @@ -12,13 +12,16 @@ env: jobs: build: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v4 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Build run: cargo build --verbose --no-default-features --features=x11,openxr - name: Run tests diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml index b1e6b2e..793fa61 100644 --- a/.github/workflows/make-release.yml +++ b/.github/workflows/make-release.yml @@ -14,13 +14,16 @@ env: jobs: make_release: runs-on: ubuntu-22.04 + defaults: + run: + working-directory: ./wlx-overlay-s steps: - uses: actions/checkout@v3 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Prepare Environment run: | - .github/workflows/scripts/appimage_prepare_env.sh + ../.github/workflows/scripts/appimage_prepare_env.sh - name: Cargo Build run: | cargo build --release diff --git a/.gitignore b/.gitignore index 265b7f5..5cd5747 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /target .gdb_history +.vscode +.cargo +scripts/translator/node_modules \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e73e544..9034d0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "ab_glyph" -version = "0.2.29" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,47 +14,77 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "alsa" version = "0.9.1" @@ -62,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", - "bitflags 2.9.0", + "bitflags 2.10.0", "cfg-if", "libc", ] @@ -84,16 +114,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.0", + "bitflags 2.10.0", "cc", "cesu8", "jni", "jni-sys", "libc", "log", - "ndk 0.9.0", + "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "thiserror 1.0.69", ] @@ -104,12 +134,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -131,9 +155,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -146,44 +170,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "appendlist" @@ -200,15 +224,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "aquamarine" version = "0.1.12" @@ -222,6 +237,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "arraydeque" version = "0.5.1" @@ -246,6 +278,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -257,16 +298,16 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +checksum = "da0986d5b4f0802160191ad75f8d33ada000558757db3defb70299ca95d9fcbd" dependencies = [ "async-fs", "async-net", "enumflags2", "futures-channel", "futures-util", - "rand 0.9.0", + "rand", "serde", "serde_repr", "url", @@ -287,9 +328,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -299,22 +340,23 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] [[package]] name = "async-fs" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ "async-lock", "blocking", @@ -323,28 +365,27 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix 1.1.2", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ "event-listener", "event-listener-strategy", @@ -364,9 +405,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ "async-channel", "async-io", @@ -377,8 +418,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix 0.38.44", - "tracing", + "rustix 1.1.2", ] [[package]] @@ -389,14 +429,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "async-signal" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ "async-io", "async-lock", @@ -404,10 +444,10 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.44", + "rustix 1.1.2", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -418,13 +458,36 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib 0.18.5", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps 6.2.2", ] [[package]] @@ -433,6 +496,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic_float" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" + [[package]] name = "atty" version = "0.2.14" @@ -446,9 +515,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "autocxx" @@ -468,7 +537,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7d41cf081e31a74378456586b47a5bae2b75a98e5f7c248c9d9bf433e3637f4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -478,7 +547,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -490,7 +559,7 @@ dependencies = [ "autocxx-engine", "env_logger", "indexmap 1.9.3", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -516,7 +585,7 @@ dependencies = [ "regex", "rustversion", "serde_json", - "syn 2.0.100", + "syn 2.0.111", "tempfile", "thiserror 1.0.69", "version_check", @@ -532,7 +601,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -549,15 +618,58 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.100", + "syn 2.0.111", "thiserror 1.0.69", ] [[package]] -name = "backtrace" -version = "0.3.74" +name = "av-scenechange" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.17", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -565,14 +677,14 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bcdec_rs" @@ -587,7 +699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "annotate-snippets", - "bitflags 2.9.0", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -600,27 +712,15 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.100", + "syn 2.0.111", "which", ] [[package]] -name = "bindgen" -version = "0.70.1" +name = "bit_field" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.9.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.100", -] +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" @@ -630,11 +730,20 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", +] + +[[package]] +name = "bitstream-io" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +dependencies = [ + "core2", ] [[package]] @@ -656,10 +765,19 @@ dependencies = [ ] [[package]] -name = "blocking" -version = "1.6.1" +name = "block2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.3", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ "async-channel", "async-task", @@ -669,29 +787,35 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.17.0" +name = "built" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -701,10 +825,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "bytes" -version = "1.10.1" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.10.0", + "cairo-sys-rs", + "glib 0.18.5", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps 6.2.2", +] [[package]] name = "calloop" @@ -712,7 +867,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "log", "polling", "rustix 0.38.44", @@ -722,13 +877,13 @@ dependencies = [ [[package]] name = "calloop" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10929724661d1c43856fd87c7a127ae944ec55579134fb485e4136fb6a46fdcb" +checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "polling", - "rustix 0.38.44", + "rustix 1.1.2", "slab", "tracing", ] @@ -746,11 +901,24 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.2.18" +name = "calloop-wayland-source" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" dependencies = [ + "calloop 0.14.3", + "rustix 1.1.2", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -768,7 +936,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -778,14 +946,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", - "target-lexicon", + "target-lexicon 0.12.16", +] + +[[package]] +name = "cfg-expr" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9acd0bdbbf4b2612d09f52ba61da432140cb10930354079d0d53fafc12968726" +dependencies = [ + "smallvec", + "target-lexicon 0.13.3", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -799,45 +977,33 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" dependencies = [ - "approx 0.4.0", + "approx", "num-traits", ] [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", ] [[package]] name = "chrono-tz" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" dependencies = [ "chrono", - "chrono-tz-build", "phf", ] -[[package]] -name = "chrono-tz-build" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402" -dependencies = [ - "parse-zoneinfo", - "phf_codegen", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -851,9 +1017,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -861,9 +1027,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -873,21 +1039,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" @@ -900,20 +1066,26 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.0", + "unicode-width 0.2.2", ] [[package]] -name = "colorchoice" -version = "1.0.3" +name = "color_quant" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "combine" @@ -936,29 +1108,24 @@ dependencies = [ [[package]] name = "config" -version = "0.15.11" +version = "0.15.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595aae20e65c3be792d05818e8c63025294ac3cb7e200f11459063a352a6ef80" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" dependencies = [ "async-trait", "convert_case 0.6.0", - "json5", + "json5 0.4.1", "pathdiff", "ron", "rust-ini", - "serde", + "serde-untagged", + "serde_core", "serde_json", - "toml", - "winnow", + "toml 0.9.8", + "winnow 0.7.14", "yaml-rust2", ] -[[package]] -name = "const-cstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" - [[package]] name = "const-random" version = "0.1.18" @@ -974,7 +1141,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -1044,42 +1211,81 @@ dependencies = [ ] [[package]] -name = "coreaudio-rs" -version = "0.11.3" +name = "core2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" dependencies = [ - "bitflags 1.3.2", - "core-foundation-sys", - "coreaudio-sys", + "memchr", ] [[package]] -name = "coreaudio-sys" -version = "0.2.16" +name = "core_maths" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" dependencies = [ - "bindgen 0.70.1", + "libm", +] + +[[package]] +name = "coreaudio-rs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" +dependencies = [ + "bitflags 1.3.2", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cosmic-text" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737" +dependencies = [ + "bitflags 2.10.0", + "fontdb", + "harfrust", + "linebender_resource_handle", + "log", + "rangemap", + "rustc-hash 1.1.0", + "self_cell", + "skrifa", + "smol_str", + "swash", + "sys-locale", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", ] [[package]] name = "cpal" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" dependencies = [ "alsa", - "core-foundation-sys", "coreaudio-rs", "dasp_sample", "jni", "js-sys", "libc", "mach2", - "ndk 0.8.0", + "ndk", "ndk-context", - "oboe", + "num-derive", + "num-traits", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1095,6 +1301,34 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -1112,15 +1346,15 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1128,82 +1362,101 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.6" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" +checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790" dependencies = [ - "nix 0.29.0", - "windows-sys 0.59.0", + "dispatch2", + "nix 0.30.1", + "windows-sys 0.61.2", ] [[package]] name = "cursor-icon" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "cxx" -version = "1.0.156" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa3a202fc4f3dd6d2ce5a2f87b04fb2becc00f5643ee9c4743ba10777efb314f" +checksum = "a7620f6cfc4dcca21f2b085b7a890e16c60fd66f560cd69ee60594908dc72ab1" dependencies = [ "cc", + "cxx-build", "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", - "foldhash", + "foldhash 0.2.0", "link-cplusplus", ] [[package]] -name = "cxx-gen" -version = "0.7.156" +name = "cxx-build" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b9e50968a66aa69ce1ac23bb1b30e1b728adfe3217b0e9267b018a71297a0d" +checksum = "7a9bc1a22964ff6a355fbec24cf68266a0ed28f8b84c0864c386474ea3d0e479" dependencies = [ + "cc", "codespan-reporting", + "indexmap 2.12.1", "proc-macro2", "quote", - "syn 2.0.100", + "scratch", + "syn 2.0.111", +] + +[[package]] +name = "cxx-gen" +version = "0.7.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e660ae76bd5fcfe7baa8510b93bdb684be66a5f84b72fd91eb7f90ad24a7c3b" +dependencies = [ + "codespan-reporting", + "indexmap 2.12.1", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.156" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8cefbebcb74ed0b4a08b76139e6c29d8884a0bb94d02c6f35de821a14a6e39" +checksum = "b1f29a879d35f7906e3c9b77d7a1005a6a0787d330c09dfe4ffb5f617728cb44" dependencies = [ "clap", "codespan-reporting", + "indexmap 2.12.1", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "cxxbridge-flags" -version = "1.0.156" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604e3eff62e2f27289d618f621491a068330c3c9f8eb06555dabc292c123596e" +checksum = "d67109015f93f683e364085aa6489a5b2118b4a40058482101d699936a7836d6" [[package]] name = "cxxbridge-macro" -version = "1.0.156" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "130c3a05501d9c15dedbf08f2ff9af60f8e78422e3dffac1f43e2d83c5b489a1" +checksum = "d187e019e7b05a1f3e69a8396b70800ee867aa9fc2ab972761173ccee03742df" dependencies = [ + "indexmap 2.12.1", "proc-macro2", "quote", - "rustversion", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "darling" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ "darling_core", "darling_macro", @@ -1211,26 +1464,43 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "dash-frontend" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "gio 0.21.5", + "glam", + "gtk", + "log", + "rust-embed", + "serde", + "serde_json", + "wgui", + "wlx-common", ] [[package]] @@ -1240,14 +1510,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] -name = "dbus" -version = "0.9.7" +name = "data-url" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "dbus" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9" dependencies = [ "libc", "libdbus-sys", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -1256,7 +1532,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479dfe1e6737aa9e96c6ac7b69689dc4c32da8383f2c12744739d76afa8b66c4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "byteorder", "enum-primitive-derive", "num-traits", @@ -1264,24 +1540,24 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1300,6 +1576,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.10.0", + "block2 0.6.2", + "libc", + "objc2 0.6.3", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1308,7 +1596,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1334,13 +1622,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1366,9 +1654,9 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" @@ -1376,7 +1664,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1427,9 +1715,9 @@ dependencies = [ [[package]] name = "endi" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" [[package]] name = "enum-primitive-derive" @@ -1444,9 +1732,9 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -1454,34 +1742,34 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "enumset" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.10.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1497,6 +1785,26 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1504,20 +1812,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "errno" -version = "0.3.11" +name = "erased-serde" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", ] [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1534,6 +1872,27 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + [[package]] name = "fastrand" version = "2.3.0" @@ -1541,10 +1900,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "fixedbitset" -version = "0.4.2" +name = "fdeflate" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flagset" @@ -1552,6 +1936,22 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "fnv" version = "1.0.7" @@ -1565,15 +1965,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "fontconfig-rs" -version = "0.1.1" +name = "foldhash" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4baadad5111c6820e97fc8bde5077258e6f272b5b38538db4b42e1812f29f3" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "font-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" dependencies = [ - "const-cstr", - "once_cell", - "thiserror 1.0.69", - "yeslogic-fontconfig-sys", + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", ] [[package]] @@ -1594,7 +2020,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1605,35 +2031,13 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] -[[package]] -name = "freetype-rs" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5442dee36ca09604133580dc0553780e867936bb3cbef3275859e889026d2b17" -dependencies = [ - "bitflags 2.9.0", - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "futures" version = "0.3.31" @@ -1684,9 +2088,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1703,7 +2107,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1736,6 +2140,64 @@ dependencies = [ "slab", ] +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio 0.18.4", + "glib 0.18.5", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio 0.18.4", + "glib 0.18.5", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "pkg-config", + "system-deps 6.2.2", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1748,42 +2210,114 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.2", + "windows-link 0.2.1", ] [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", +] + +[[package]] +name = "gif" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +dependencies = [ + "color_quant", + "weezl", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.18.1", + "glib 0.18.5", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ff48bf600c68b476e61dc6b7c762f2f4eb91deef66583ba8bb815c30b5811a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.21.5", + "glib 0.21.5", + "libc", + "pin-project-lite", + "smallvec", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps 6.2.2", + "winapi", +] + +[[package]] +name = "gio-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22" +dependencies = [ + "glib-sys 0.21.5", + "gobject-sys 0.21.5", + "libc", + "system-deps 7.0.7", + "windows-sys 0.61.2", +] [[package]] name = "gl_generator" @@ -1798,30 +2332,214 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.1" +version = "0.30.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3aa70d918d2b234126ff4f850f628f172542bf0603ded26b8ee36e5e22d5f9" +checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" dependencies = [ - "approx 0.5.1", "mint", - "serde", + "serde_core", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.10.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.18.1", + "glib-macros 0.18.5", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" +dependencies = [ + "bitflags 2.10.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.21.5", + "glib-macros 0.21.5", + "glib-sys 0.21.5", + "gobject-sys 0.21.5", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "glib-macros" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "glib-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c" +dependencies = [ + "libc", + "system-deps 7.0.7", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "gobject-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294" +dependencies = [ + "glib-sys 0.21.5", + "libc", + "system-deps 7.0.7", +] + +[[package]] +name = "grid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio 0.18.4", + "glib 0.18.5", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "system-deps 6.2.2", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.111", +] [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "bytemuck", "cfg-if", "crunchy", + "zerocopy", +] + +[[package]] +name = "harfrust" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "core_maths", + "read-fonts", + "smallvec", ] [[package]] @@ -1838,11 +2556,22 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -1851,7 +2580,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.5", ] [[package]] @@ -1877,9 +2606,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1889,11 +2618,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1904,15 +2633,15 @@ checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1920,7 +2649,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.62.2", ] [[package]] @@ -1934,21 +2663,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1957,99 +2687,61 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -2058,9 +2750,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idmap" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba885f996064df334b1639785897d1c58c0646750101b839b8359664cb26c0e" +checksum = "1f92530a7d4564864380ca6747ef14312d264a342d1e6ada9e754debb2c789d4" dependencies = [ "fixedbitset", "serde", @@ -2068,20 +2760,20 @@ dependencies = [ [[package]] name = "idmap-derive" -version = "0.1.2" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f44aa1b60e7de7e2833ad2cc3bc79880171d1e1efdb44ca833a1acf8102870" +checksum = "ac03bebb34669b3ef9c04ab65926ddf3513f5b5b74b45c540bf250829a1e92eb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.111", ] [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2090,14 +2782,45 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] +[[package]] +name = "image" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.0", + "ravif", + "rayon", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + [[package]] name = "image_dds" version = "0.7.2" @@ -2111,6 +2834,18 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imgref" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" + [[package]] name = "indexmap" version = "1.9.3" @@ -2124,12 +2859,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.1", ] [[package]] @@ -2158,6 +2893,17 @@ dependencies = [ "nix 0.29.0", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "interprocess" version = "2.2.3" @@ -2173,9 +2919,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -2213,6 +2959,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -2243,19 +2998,19 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2278,6 +3033,16 @@ dependencies = [ "serde", ] +[[package]] +name = "json5" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c86c72f9e1d3fe29baa32cab8896548eef9aae271fce4e796d16b583fdf6d5" +dependencies = [ + "serde", + "ucd-trie", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -2294,6 +3059,17 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2307,37 +3083,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] -name = "libc" -version = "0.2.171" +name = "lebe" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libdbus-sys" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f" dependencies = [ "pkg-config", ] [[package]] -name = "libloading" -version = "0.8.6" +name = "libfuzzer-sys" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "libmonado" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f56d8582a273a05076c57d5478faa51c39958744760938f2da770e890b43c62d" dependencies = [ - "bindgen 0.69.5", + "bindgen", "cmake", "convert_case 0.6.0", "dlopen2", @@ -2347,18 +3145,18 @@ dependencies = [ "semver", "serde", "serde_json", - "xdg", + "xdg 2.5.2", ] [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.5.18", ] [[package]] @@ -2366,15 +3164,15 @@ name = "libspa" version = "0.8.0" source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git#ba32202c3c391004c3bb533b58fa75a50e47ff57" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cc", "convert_case 0.6.0", "cookie-factory", "libc", "libspa-sys", "nix 0.27.1", - "nom", - "system-deps", + "nom 7.1.3", + "system-deps 6.2.2", ] [[package]] @@ -2382,16 +3180,22 @@ name = "libspa-sys" version = "0.8.0" source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git#ba32202c3c391004c3bb533b58fa75a50e47ff57" dependencies = [ - "bindgen 0.69.5", + "bindgen", "cc", - "system-deps", + "system-deps 6.2.2", ] [[package]] -name = "link-cplusplus" -version = "1.0.10" +name = "linebender_resource_handle" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" dependencies = [ "cc", ] @@ -2410,31 +3214,30 @@ checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "log-panics" @@ -2447,43 +3250,62 @@ dependencies = [ ] [[package]] -name = "mach2" -version = "0.4.2" +name = "loop9" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -2517,7 +3339,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -2528,11 +3350,12 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2551,17 +3374,13 @@ dependencies = [ ] [[package]] -name = "ndk" -version = "0.8.0" +name = "moxcms" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +checksum = "80986bbbcf925ebd3be54c26613d861255284584501595cf418320c078945608" dependencies = [ - "bitflags 2.9.0", - "jni-sys", - "log", - "ndk-sys 0.5.0+25.2.9519653", - "num_enum", - "thiserror 1.0.69", + "num-traits", + "pxfm", ] [[package]] @@ -2570,10 +3389,10 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "jni-sys", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -2585,15 +3404,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys", -] - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -2603,13 +3413,19 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cfg-if", "libc", ] @@ -2620,7 +3436,19 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -2638,12 +3466,27 @@ dependencies = [ ] [[package]] -name = "normpath" -version = "1.3.0" +name = "nom" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" dependencies = [ - "windows-sys 0.59.0", + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "normpath" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b" +dependencies = [ + "windows-sys 0.61.2", ] [[package]] @@ -2657,12 +3500,21 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", ] [[package]] @@ -2679,7 +3531,27 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", ] [[package]] @@ -2693,23 +3565,24 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -2730,9 +3603,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", ] @@ -2743,8 +3616,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "libc", "objc2 0.5.2", "objc2-core-data", @@ -2753,14 +3626,29 @@ dependencies = [ "objc2-quartz-core 0.2.2", ] +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.10.0", + "libc", + "objc2 0.6.3", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2772,31 +3660,54 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2 0.6.3", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", +] + [[package]] name = "objc2-core-data" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.10.0", + "dispatch2", + "objc2 0.6.3", ] [[package]] @@ -2805,7 +3716,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal 0.2.2", @@ -2817,7 +3728,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -2835,8 +3746,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -2844,12 +3755,22 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", "objc2-core-foundation", ] @@ -2859,7 +3780,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-app-kit", "objc2-foundation 0.2.2", @@ -2871,21 +3792,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] [[package]] name = "objc2-metal" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c41bc8b0e50ea7a5304a56f25e0066f526e99641b46fd7b9ad4421dd35bff6" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-foundation 0.3.2", ] [[package]] @@ -2894,8 +3815,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal 0.2.2", @@ -2903,15 +3824,15 @@ dependencies = [ [[package]] name = "objc2-quartz-core" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.10.0", + "objc2 0.6.3", "objc2-core-foundation", - "objc2-foundation 0.3.0", - "objc2-metal 0.3.0", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", ] [[package]] @@ -2930,8 +3851,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", @@ -2951,7 +3872,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2962,8 +3883,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2971,42 +3892,25 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] -[[package]] -name = "oboe" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" -dependencies = [ - "jni", - "ndk 0.8.0", - "ndk-context", - "num-derive", - "num-traits", - "oboe-sys", -] - -[[package]] -name = "oboe-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" -dependencies = [ - "cc", -] - [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "openxr" version = "0.19.0" @@ -3029,9 +3933,9 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.3.48" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827" dependencies = [ "libredox", ] @@ -3057,10 +3961,28 @@ dependencies = [ ] [[package]] -name = "overload" -version = "0.1.1" +name = "ouroboros" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.111", +] [[package]] name = "ovr_overlay" @@ -3091,13 +4013,38 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser", ] +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio 0.18.4", + "glib 0.18.5", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps 6.2.2", +] + [[package]] name = "parking" version = "2.2.1" @@ -3106,9 +4053,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3116,25 +4063,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] -name = "parse-zoneinfo" -version = "0.3.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" [[package]] name = "pathdiff" @@ -3144,26 +4094,25 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", - "thiserror 2.0.12", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -3171,66 +4120,51 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ - "once_cell", "pest", "sha2", ] [[package]] name = "phf" -version = "0.11.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" dependencies = [ "phf_shared", ] -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.10" @@ -3248,7 +4182,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -3280,7 +4214,7 @@ version = "0.8.0" source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git#ba32202c3c391004c3bb533b58fa75a50e47ff57" dependencies = [ "anyhow", - "bitflags 2.9.0", + "bitflags 2.10.0", "libc", "libspa", "libspa-sys", @@ -3295,9 +4229,9 @@ name = "pipewire-sys" version = "0.8.0" source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git#ba32202c3c391004c3bb533b58fa75a50e47ff57" dependencies = [ - "bindgen 0.69.5", + "bindgen", "libspa-sys", - "system-deps", + "system-deps 6.2.2", ] [[package]] @@ -3307,18 +4241,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "polling" -version = "3.7.4" +name = "png" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags 2.10.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi 0.5.2", "pin-project-lite", - "rustix 0.38.44", - "tracing", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", ] [[package]] @@ -3333,26 +4301,45 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy", ] [[package]] name = "prettyplease" -version = "0.2.32" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ - "toml_edit", + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.9", ] [[package]] @@ -3381,32 +4368,60 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] -name = "profiling" -version = "1.0.16" +name = "proc-macro2-diagnostics" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", + "version_check", + "yansi", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.111", ] +[[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.30.0" @@ -3418,58 +4433,36 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.4" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", - "zerocopy 0.8.24", -] - -[[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 0.6.4", + "rand_chacha", + "rand_core", ] [[package]] @@ -3479,16 +4472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", + "rand_core", ] [[package]] @@ -3497,7 +4481,63 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", +] + +[[package]] +name = "rangemap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbbbbea733ec66275512d0b9694f34102e7d5406fdbe2ad8d21b28dce92887c" + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.14.0", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "thiserror 2.0.17", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", ] [[package]] @@ -3512,10 +4552,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.3", "objc2-core-foundation", - "objc2-foundation 0.3.0", - "objc2-quartz-core 0.3.0", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" +dependencies = [ + "bytemuck", + "core_maths", + "font-types", ] [[package]] @@ -3535,77 +4606,90 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] -name = "regex-syntax" -version = "0.8.5" +name = "resvg" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] [[package]] name = "rodio" -version = "0.20.1" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1" +checksum = "e40ecf59e742e03336be6a3d53755e789fd05a059fa22dfa0ed624722319e183" dependencies = [ "cpal", + "dasp_sample", "hound", + "num-rational", + "symphonia", ] [[package]] name = "ron" -version = "0.8.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" dependencies = [ - "base64", - "bitflags 2.9.0", + "bitflags 2.10.0", + "once_cell", "serde", "serde_derive", + "typeid", + "unicode-ident", ] [[package]] @@ -3615,7 +4699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd37602e1513794e952274082d074e8d31aa7f64d047e3acb746c91db40600a5" dependencies = [ "byteorder", - "nom", + "nom 7.1.3", "time", ] @@ -3629,21 +4713,69 @@ dependencies = [ ] [[package]] -name = "rust-ini" +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "roxmltree" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + +[[package]] +name = "rust-embed" +version = "8.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.111", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" dependencies = [ "cfg-if", "ordered-multimap", - "trim-in-place", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -3672,7 +4804,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3681,22 +4813,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rxscreen" @@ -3734,6 +4866,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + [[package]] name = "sctk-adwaita" version = "0.10.1" @@ -3742,47 +4880,76 @@ checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.5", - "smithay-client-toolkit", + "memmap2", + "smithay-client-toolkit 0.19.2", "tiny-skia", ] [[package]] -name = "semver" -version = "1.0.26" +name = "self_cell" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -3804,25 +4971,34 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -3831,9 +5007,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3858,7 +5034,7 @@ checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7" dependencies = [ "cmake", "libc", - "roxmltree", + "roxmltree 0.14.1", ] [[package]] @@ -3878,13 +5054,37 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -3892,14 +5092,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] -name = "slab" -version = "0.4.9" +name = "skrifa" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" dependencies = [ - "autocfg", + "bytemuck", + "read-fonts", ] +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "slabbin" version = "1.2.0" @@ -3913,20 +5120,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4f120bb98cb4cb0dab21c882968c3cbff79dd23b46f07b1cf5c25044945ce84" [[package]] -name = "smallvec" -version = "1.15.0" +name = "slotmap" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497aa82b881bf392470c286b213e01f7aa30b2fb08857831f81c87b251ba4d89" +checksum = "740cea6927892bc182d5bf70c8f79806c8bc9f68f2fb96e55a30be171b63af98" dependencies = [ "appendlist", - "bitflags 2.9.0", - "calloop 0.14.2", + "atomic_float", + "bitflags 2.10.0", + "calloop 0.14.3", "cgmath", "cursor-icon", "downcast-rs", @@ -3936,16 +5153,17 @@ dependencies = [ "encoding_rs", "errno", "gl_generator", - "indexmap 2.9.0", + "indexmap 2.12.1", "libc", "libloading", "profiling", - "rand 0.8.5", - "rustix 0.38.44", + "rand", + "rustix 1.1.2", "scopeguard", + "sha2", "smallvec", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.17", "tracing", "wayland-protocols", "wayland-protocols-misc", @@ -3961,15 +5179,13 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.0", - "bytemuck", + "bitflags 2.10.0", "calloop 0.13.0", - "calloop-wayland-source", + "calloop-wayland-source 0.3.0", "cursor-icon", "libc", "log", - "memmap2 0.9.5", - "pkg-config", + "memmap2", "rustix 0.38.44", "thiserror 1.0.69", "wayland-backend", @@ -3979,7 +5195,36 @@ dependencies = [ "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", - "xkbcommon 0.7.0", + "xkeysym", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "calloop 0.14.3", + "calloop-wayland-source 0.4.1", + "cursor-icon", + "libc", + "log", + "memmap2", + "pkg-config", + "rustix 1.1.2", + "thiserror 2.0.17", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "wayland-scanner", + "xkbcommon 0.8.0", "xkeysym", ] @@ -3994,9 +5239,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -4009,6 +5254,9 @@ name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] [[package]] name = "strsim" @@ -4018,24 +5266,110 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47846491253e976bdd07d0f9cc24b7daf24720d11309302ccbbc6e6b6e53550a" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "symphonia" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +dependencies = [ + "lazy_static", + "symphonia-codec-pcm", + "symphonia-core", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-pcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-riff" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", ] [[package]] @@ -4051,9 +5385,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -4062,26 +5396,36 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", ] [[package]] name = "sysinfo" -version = "0.34.2" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b93974b3d3aeaa036504b8eefd4c039dced109171c1ae973f1dc63b2c7e4b2" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" dependencies = [ "libc", "memchr", "ntapi", "objc2-core-foundation", - "windows 0.57.0", + "objc2-io-kit", + "windows 0.61.3", ] [[package]] @@ -4090,13 +5434,38 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr", + "cfg-expr 0.15.8", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.23", "version-compare", ] +[[package]] +name = "system-deps" +version = "7.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" +dependencies = [ + "cfg-expr 0.20.4", + "heck 0.5.0", + "pkg-config", + "toml 0.9.8", + "version-compare", +] + +[[package]] +name = "taffy" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ba83ebaf2954d31d05d67340fd46cebe99da2b7133b0dd68d70c65473a437b" +dependencies = [ + "arrayvec", + "grid", + "serde", + "slotmap", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -4104,16 +5473,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] -name = "tempfile" -version = "3.19.1" +name = "target-lexicon" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.4", "once_cell", - "rustix 1.0.5", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.2", ] [[package]] @@ -4136,11 +5511,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -4151,35 +5526,34 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -4192,15 +5566,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -4226,6 +5600,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", + "png 0.17.16", "tiny-skia-path", ] @@ -4242,53 +5617,141 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", ] [[package]] -name = "toml" -version = "0.8.20" +name = "tinyvec" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap 2.12.1", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow 0.7.14", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.9.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", + "indexmap 2.12.1", + "toml_datetime 0.6.11", + "winnow 0.5.40", ] [[package]] -name = "tracing" -version = "0.1.41" +name = "toml_edit" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.12.1", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.12.1", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "winnow 0.7.14", +] + +[[package]] +name = "toml_edit" +version = "0.23.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" +dependencies = [ + "indexmap 2.12.1", + "toml_datetime 0.7.3", + "toml_parser", + "winnow 0.7.14", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow 0.7.14", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + +[[package]] +name = "tracing" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -4298,20 +5761,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -4330,14 +5793,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -4346,23 +5809,26 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "ttf-parser" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -4382,10 +5848,44 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.18" +name = "uidev" +version = "0.1.0" +dependencies = [ + "anyhow", + "dash-frontend", + "glam", + "log", + "rust-embed", + "tracing-subscriber", + "vulkano", + "vulkano-shaders", + "wgui", + "winit", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" @@ -4401,9 +5901,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unsafe-libyaml" @@ -4413,9 +5913,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -4424,10 +5924,26 @@ dependencies = [ ] [[package]] -name = "utf16_iter" -version = "1.0.5" +name = "usvg" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64", + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "xmlwriter", +] [[package]] name = "utf8_iter" @@ -4443,12 +5959,26 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom 0.3.2", - "rand 0.9.0", + "getrandom 0.3.4", + "js-sys", + "rand", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", ] [[package]] @@ -4459,9 +5989,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "version-compare" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "version_check" @@ -4480,19 +6010,19 @@ dependencies = [ [[package]] name = "vulkano" -version = "0.35.1" +version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08840c2b51759a6f88f26f5ea378bc8b5c199a5b4760ddda292304be087249c4" +checksum = "ee487adf7c13e5aa46d4d7fac6ffb8e172eb7e6472aaf815e9ade4992f8f2414" dependencies = [ "ash", "bytemuck", "crossbeam-queue", - "foldhash", + "foldhash 0.1.5", "half", "heck 0.4.1", - "indexmap 2.9.0", + "indexmap 2.12.1", "libloading", - "nom", + "nom 7.1.3", "once_cell", "parking_lot", "proc-macro2", @@ -4506,8 +6036,6 @@ dependencies = [ "thread_local", "vk-parse", "vulkano-macros", - "x11-dl", - "x11rb", ] [[package]] @@ -4516,10 +6044,10 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dc929c42c9336fd082079ac3ea30126e4a0dfe36fd2e2b3581303f7d140d20f" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -4528,12 +6056,12 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf501461be7cef2893c0e62c50945add9763cc482051d29053f6157089d5ea9" dependencies = [ - "foldhash", + "foldhash 0.1.5", "heck 0.4.1", "proc-macro2", "quote", "shaderc", - "syn 2.0.100", + "syn 2.0.111", "vulkano", ] @@ -4549,50 +6077,37 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -4603,9 +6118,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4613,35 +6128,35 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.100", - "wasm-bindgen-backend", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.1.2", "scoped-tls", "smallvec", "wayland-sys", @@ -4649,12 +6164,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.0", - "rustix 0.38.44", + "bitflags 2.10.0", + "rustix 1.1.2", "wayland-backend", "wayland-scanner", ] @@ -4665,27 +6180,27 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.1.2", "wayland-client", "xcursor", ] [[package]] name = "wayland-egl" -version = "0.32.5" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504838241a10e271f48ffd429ac4033e0ac468b399fe7c2e2840f5c3a82d9902" +checksum = "d36232ee23ba3ea34a6835d68ca1af91d3ca3d6eddcf9c7147c4e0e66901b9fd" dependencies = [ "wayland-backend", "wayland-sys", @@ -4693,11 +6208,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.6" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4705,13 +6220,27 @@ dependencies = [ ] [[package]] -name = "wayland-protocols-misc" -version = "0.3.6" +name = "wayland-protocols-experimental" +version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb7ee1810026d1bb15d47086d03a7e5c68651c707e305ba1e8cc796fcbf5a54" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfe33d551eb8bffd03ff067a8b44bb963919157841a99957151299a6307d19c" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", "wayland-protocols", "wayland-scanner", "wayland-server", @@ -4719,11 +6248,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4732,11 +6261,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4746,33 +6275,33 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", - "quick-xml 0.37.4", + "quick-xml 0.37.5", "quote", ] [[package]] name = "wayland-server" -version = "0.31.7" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fabd7ed68cff8e7657b8a8a1fbe90cb4a3f0c30d90da4bf179a7a23008a4cb" +checksum = "fcbd4f3aba6c9fba70445ad2a484c0ef0356c1a9459b1e8e435bedc1971a6222" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "downcast-rs", - "rustix 0.38.44", + "rustix 1.1.2", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -4795,9 +6324,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -4813,6 +6342,39 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgui" +version = "0.1.0" +dependencies = [ + "anyhow", + "cosmic-text", + "etagere", + "flate2", + "glam", + "image", + "log", + "lru", + "ouroboros", + "parking_lot", + "regex", + "resvg", + "roxmltree 0.21.1", + "rust-embed", + "rustc-hash 2.1.1", + "serde_json", + "slotmap", + "smallvec", + "taffy", + "vulkano", + "vulkano-shaders", +] + [[package]] name = "which" version = "4.4.2" @@ -4827,9 +6389,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -4849,11 +6411,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4874,12 +6436,24 @@ dependencies = [ [[package]] name = "windows" -version = "0.57.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", ] [[package]] @@ -4894,78 +6468,84 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.57.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings", + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] name = "windows-implement" -version = "0.57.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "windows-interface" -version = "0.57.0" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] [[package]] name = "windows-result" @@ -4978,20 +6558,38 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -5021,6 +6619,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -5036,21 +6643,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5067,18 +6659,21 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5091,12 +6686,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5109,12 +6698,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5133,12 +6716,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5151,12 +6728,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5169,12 +6740,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5187,12 +6752,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5201,15 +6760,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.9" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.0", - "block2", + "bitflags 2.10.0", + "block2 0.5.1", "bytemuck", "calloop 0.13.0", "cfg_aliases", @@ -5220,8 +6779,8 @@ dependencies = [ "dpi", "js-sys", "libc", - "memmap2 0.9.5", - "ndk 0.9.0", + "memmap2", + "ndk", "objc2 0.5.2", "objc2-app-kit", "objc2-foundation 0.2.2", @@ -5233,7 +6792,7 @@ dependencies = [ "redox_syscall 0.4.1", "rustix 0.38.44", "sctk-adwaita", - "smithay-client-toolkit", + "smithay-client-toolkit 0.19.2", "smol_str", "tracing", "unicode-segmentation", @@ -5253,26 +6812,31 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.6" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "winnow" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ - "bitflags 2.9.0", + "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "wlx-capture" -version = "0.5.4" -source = "git+https://github.com/galister/wlx-capture?tag=v0.5.4#1aad584fafd529c63cce3355f0b2e2175ab69c70" +version = "0.5.3" dependencies = [ "ashpd", "drm-fourcc", @@ -5281,11 +6845,22 @@ dependencies = [ "log", "pipewire", "rxscreen", - "smithay-client-toolkit", + "smithay-client-toolkit 0.20.0", "wayland-client", "wayland-protocols", ] +[[package]] +name = "wlx-common" +version = "0.1.0" +dependencies = [ + "chrono", + "glam", + "idmap", + "idmap-derive", + "serde", +] + [[package]] name = "wlx-overlay-s" version = "25.4.2" @@ -5299,8 +6874,6 @@ dependencies = [ "config", "ctrlc", "dbus", - "fontconfig-rs", - "freetype-rs", "futures", "glam", "idmap", @@ -5309,7 +6882,7 @@ dependencies = [ "input-linux", "interprocess", "json", - "json5", + "json5 1.3.0", "khronos-egl", "libc", "libmonado", @@ -5321,15 +6894,17 @@ dependencies = [ "regex", "rodio", "rosc", + "rust-embed", "serde", "serde_json", "serde_json5", "serde_yaml", + "slotmap", "smallvec", "smithay", "strum", "sysinfo", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "tracing-subscriber", "uuid", @@ -5338,24 +6913,20 @@ dependencies = [ "wayland-client", "wayland-egl", "wayvr_ipc", + "wgui", "winit", "wlx-capture", + "wlx-common", "xcb", - "xdg", - "xkbcommon 0.8.0", + "xdg 3.0.0", + "xkbcommon 0.9.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "x11-dl" @@ -5370,30 +6941,30 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", "libloading", "once_cell", - "rustix 0.38.44", + "rustix 1.1.2", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcb" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e2f212bb1a92cd8caac8051b829a6582ede155ccb60b5d5908b81b100952be" +checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" dependencies = [ "as-raw-xcb-connection", "bitflags 1.3.2", @@ -5403,9 +6974,9 @@ dependencies = [ [[package]] name = "xcursor" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xdg" @@ -5414,35 +6985,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] -name = "xdg-home" -version = "1.3.0" +name = "xdg" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "xkbcommon" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" -dependencies = [ - "libc", - "memmap2 0.8.0", - "xkeysym", -] +checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" [[package]] name = "xkbcommon" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" +dependencies = [ + "libc", + "memmap2", + "xkeysym", +] + +[[package]] +name = "xkbcommon" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "as-raw-xcb-connection", "libc", - "memmap2 0.9.5", + "memmap2", "xkeysym", ] @@ -5452,7 +7019,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "dlib", "log", "once_cell", @@ -5470,9 +7037,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xmlparser" @@ -5481,16 +7048,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] -name = "yaml-rust2" -version = "0.10.1" +name = "xmlwriter" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818913695e83ece1f8d2a1c52d54484b7b46d0f9c06beeb2649b9da50d9b512d" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "yaml-rust2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" dependencies = [ "arraydeque", "encoding_rs", "hashlink", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yansi-term" version = "0.1.2" @@ -5501,24 +7086,17 @@ dependencies = [ ] [[package]] -name = "yeslogic-fontconfig-sys" -version = "3.2.0" +name = "yazi" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" -dependencies = [ - "const-cstr", - "dlib", - "once_cell", - "pkg-config", -] +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5526,25 +7104,24 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", "synstructure", ] [[package]] name = "zbus" -version = "5.5.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" dependencies = [ "async-broadcast", "async-executor", - "async-fs", "async-io", "async-lock", "async-process", @@ -5557,16 +7134,15 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix 0.29.0", + "nix 0.30.1", "ordered-stream", "serde", "serde_repr", - "static_assertions", "tracing", "uds_windows", - "windows-sys 0.59.0", - "winnow", - "xdg-home", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.14", "zbus_macros", "zbus_names", "zvariant", @@ -5574,14 +7150,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.5.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", "zbus_names", "zvariant", "zvariant_utils", @@ -5595,48 +7171,34 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow", + "winnow 0.7.14", "zvariant", ] [[package]] -name = "zerocopy" -version = "0.7.35" +name = "zeno" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5656,15 +7218,26 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", "synstructure", ] [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -5673,54 +7246,76 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "zune-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f520eebad972262a1dde0ec455bce4f8b298b1e5154513de58c114c4c54303e8" +dependencies = [ + "zune-core", ] [[package]] name = "zvariant" -version = "5.4.0" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", "url", - "winnow", + "winnow 0.7.14", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.4.0" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", - "syn 2.0.100", - "winnow", + "syn 2.0.111", + "winnow 0.7.14", ] diff --git a/Cargo.toml b/Cargo.toml index d3f5fb6..5c89cda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,120 +1,36 @@ +[profile.dev] +opt-level = 1 +debug = true +strip = "none" +debug-assertions = true +incremental = true + [profile.release-with-debug] inherits = "release" debug = true -[package] -name = "wlx-overlay-s" -version = "25.4.2" -edition = "2021" -license = "GPL-3.0-only" -authors = ["galister"] -description = "Access your Wayland/X11 desktop from Monado/WiVRn/SteamVR. Now with Vulkan!" -repository = "https://github.com/galister/wlx-overlay-s" -keywords = ["linux", "openvr", "openxr", "x11", "wayland", "openvr-overlay", "openxr-overlay"] -categories = ["games"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.89" -ash = "^0.38.0" # must match vulkano -chrono = "0.4.38" -chrono-tz = "0.10.0" -clap = { version = "4.5.6", features = ["derive"] } -config = "0.15.11" -ctrlc = { version = "3.4.4", features = ["termination"] } -dbus = { version = "0.9.7" } -fontconfig-rs = "0.1.1" -freetype-rs = "0.36.0" # latest version supported on ubuntu 22.04 -futures = "0.3.30" -glam = { version = "0.30.1", features = ["approx", "mint", "serde"] } -idmap = { version = "0.2.21", features = ["serde"] } -idmap-derive = "0.1.2" -input-linux = "0.7.0" -json = { version = "0.12.4", optional = true } -json5 = "0.4.1" -libc = "0.2.155" -log = "0.4.21" -openxr = { git = "https://github.com/Ralith/openxrs", rev = "d0afdd3365bc1e14de28f6a3a21f457e788a702e", features = [ - "linked", - "mint", -], optional = true } -ovr_overlay = { features = [ - "ovr_input", - "ovr_system", -], git = "https://github.com/galister/ovr_overlay_oyasumi", optional = true } -regex = "1.11.1" -rodio = { version = "0.20.1", default-features = false, features = [ - "wav", - "hound", -] } -rosc = { version = "0.11.4", optional = true } -serde = { version = "1.0.203", features = ["derive", "rc"] } -serde_json = "1.0.117" -serde_yaml = "0.9.34" -smallvec = "1.13.2" -strum = { version = "0.27.1", features = ["derive"] } -sysinfo = { version = "0.34.2" } -thiserror = "2.0.3" -vulkano = { version = "0.35.1" } -vulkano-shaders = { version = "0.35.0" } -wlx-capture = { git = "https://github.com/galister/wlx-capture", tag = "v0.5.4", default-features = false } -libmonado = { version = "1.3.2", optional = true } -winit = { version = "0.30.0", optional = true } -xdg = "2.5.2" -log-panics = { version = "2.1.0", features = ["with-backtrace"] } -serde_json5 = "0.2.1" -xkbcommon = { version = "0.8.0" } -xcb = { version = "1.4.0", optional = true, features = [ - "as-raw-xcb-connection", -] } -image_dds = { version = "0.7.2", default-features = false, features = [ - "ddsfile", -] } -mint = "0.5.9" -tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -tracing = "0.1.41" - -################################ -#WayVR-only deps -################################ -khronos-egl = { version = "6.0.0", features = ["static"], optional = true } -smithay = { version = "0.5.1", default-features = false, features = [ - "renderer_gl", - "backend_egl", - "backend_drm", - "xwayland", - "wayland_frontend", -], optional = true } -uuid = { version = "1.10.0", features = ["v4", "fast-rng"], optional = true } -wayland-client = { version = "0.31.6", optional = true } -wayland-egl = { version = "0.32.4", optional = true } -interprocess = { version = "2.2.2", optional = true } -bytes = { version = "1.9.0", optional = true } -wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "a72587d23f3bb8624d9aeb1f13c0a21e65350f51", default-features = false, optional = true } -################################ - -[build-dependencies] -regex = { version = "1.11.1" } - -[features] -default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"] -openvr = ["dep:ovr_overlay", "dep:json"] -openxr = ["dep:openxr", "dep:libmonado"] -osc = ["dep:rosc"] -x11 = ["dep:xcb", "wlx-capture/xshm", "xkbcommon/x11"] -wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"] -pipewire = ["wlx-capture/pipewire"] -uidev = ["dep:winit"] -xcb = ["dep:xcb"] -wayvr = [ - "dep:khronos-egl", - "dep:smithay", - "dep:uuid", - "dep:wayland-client", - "dep:wayland-egl", - "dep:interprocess", - "dep:bytes", - "dep:wayvr_ipc", +[workspace] +members = [ + "uidev", + "wgui", + "wlx-common", + "wlx-overlay-s", + "wlx-capture", + "dash-frontend", ] -as-raw-xcb-connection = [] +resolver = "3" + +[workspace.dependencies] +anyhow = "1.0.100" +glam = { version = "0.30.9", features = ["mint", "serde"] } +idmap = "0.2.2" +idmap-derive = "0.2.22" +log = "0.4.29" +regex = "1.12.2" +rust-embed = "8.9.0" +slotmap = "1.1.1" +vulkano = { version = "0.35.2", default-features = false, features = [ + "macros", +] } +vulkano-shaders = "0.35.0" +wayland-client = { version = "0.31.11" } diff --git a/README.md b/README.md index b68ea23..dd0bff2 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,18 @@ Questions/issues specific to WlxOverlay-S will be handled in the `wlxoverlay` ch ## Setup +### Installation + +There are multiple ways to install WlxOverlay-S: +1. AppImage: Download from [Releases](https://github.com/galister/wlx-overlay-s/releases) +1. AUR package: [wlx-overlay-s-git](https://aur.archlinux.org/packages/wlx-overlay-s-git) +1. Homebrew: + 1. Add AtomicXR tap: `brew tap matrixfurry.com/atomicxr https://tangled.sh/@matrixfurry.com/homebrew-atomicxr` + 1. Install WlxOverlay-S: `brew install wlx-overlay-s` +1. [Building from source](https://github.com/galister/wlx-overlay-s/wiki/Building-from-Source). + ### General Setup -1. Grab the latest AppImage from [Releases](https://github.com/galister/wlx-overlay-s/releases). -1. `chmod +x WlxOverlay-S-*.AppImage` 1. Start Monado, WiVRn or SteamVR. 1. Run the overlay @@ -41,30 +49,25 @@ For users specifically running **SteamVR via Steam Flatpak**, follow these steps 1. Start SteamVR. 1. `flatpak run --command='/path/to/squashfs-root/AppRun' com.valvesoftware.Steam` -### Install via Homebrew - -1. Add AtomicXR tap `brew tap matrixfurry.com/atomicxr https://tangled.sh/@matrixfurry.com/homebrew-atomicxr` -2. Install WlxOverlay-S: `brew install wlx-overlay-s` - -AUR package is [wlx-overlay-s-git](https://aur.archlinux.org/packages/wlx-overlay-s-git). - -You may also want to [build from source](https://github.com/galister/wlx-overlay-s/wiki/Building-from-Source). ## First Start -**When the screen share pop-up appears, check the terminal and select the screens in the order it requests.** +**When the screen share pop-up appears, check your notifications or the terminal and select the screens in the order it requests.** In case screens were selected in the wrong order: - `rm ~/.config/wlxoverlay/conf.d/pw_tokens.yaml` then restart -**SteamVR users**: WlxOverlay-S will register itself for auto-start, so there is no need to start it every time. + +**WiVRn users**: Select WlxOverlay-S from the `Application` drop-down. If there's no such entry, select `Custom` and browse to your WlxOverlay-S executable or AppImage. **Envision users**: Go to the Plugins menu and select the WlxOverlay-S plugin. This will download and run the AppImage version of the overlay. -In order to run a standalone installation (for instance from the AUR), create a bash script containing `wlx-overlay-s --openxr --show` and then select this bash script as a custom Envision plugin. +In order to run a standalone installation (for instance from the AUR), create a bash script containing `wlx-overlay-s --openxr --show` and then set this bash script as a custom Envision plugin. This will show a home environment with headset passthrough by default or a [customizable background](https://github.com/galister/wlx-overlay-s/wiki/OpenXR-Skybox)! +**SteamVR users**: WlxOverlay-S will register itself for auto-start, so there is no need to start it every time. Disclaimer: SteamVR will sometimes disregard this and not start Wlx anyway. + **Please continue reading the guide below.** ## Getting Started @@ -76,10 +79,11 @@ The working set consists of all currently selected overlays; screens, mirrors, k The working set appears in front of the headset when shown, and can be re-centered by hiding and showing again. Show and hide the working set using: - - Non-vive controller: double-tap B or Y on the left controller. - Vive controller: double-tap the menu button on the left controller (for SteamVR, the `showhide` binding must be bound) +See the [bindings](#default-bindings) section on how to grab, move and resize overlay windows. + ### Pointer Modes AKA Laser Colors Much of the functionality in WlxOverlay-S depends on what color of laser is used to interact with a UI element. \ @@ -93,27 +97,33 @@ Please see the bindings section below on how to activate these modes. The guide here uses the colors for ease of getting started. -### The Watch +### The watch Check your left wrist for the watch. The watch is the primary tool for controlling the app. +The top of the watch shows device batteries, and the bottom shows your overlay controls. + +Enter edit mode (leftmost button on bottom) to edit your overlay sets. + +While in edit mode, the watch can also be grabbed, and passed between your hands. + +After grabbing, the watch will automatically attach to the hand that's opposite from the one that held it. + +In edit mode, try hovering other overlays to see their advanced options! + ![Watch usage guide](https://github.com/galister/wlx-overlay-s/blob/guide/wlx-watch.png) -### The Screens +### The screens Hovering a pointer over a screen will move the mouse. If there are more than one pointers hovering a screen, the pointer that was last used to click will take precedence. -The click depends on the laser color: +The click type depends on the laser color: - Blue laser: Left click - Orange laser: Right click - Purple laser: Middle click - Stick up/down: Scroll wheel -To **curve a screen**, grab it with one hand. Then, using the other hand, hover the laser over the screen and use the scroll action. - -See the [bindings](#default-bindings) section on how to grab, move and resize screens. - ### The keyboard The keyboard is fully customizable via the [keyboard.yaml](https://raw.githubusercontent.com/galister/wlx-overlay-s/main/src/res/keyboard.yaml) file. \ @@ -123,7 +133,7 @@ Typing - Use the BLUE laser when typing regularly. - While using ORANGE laser, all keystrokes will have SHIFT applied. -- Purple laser has no effect as of now. +- Purple laser is customizable via the `keyboard.yaml`'s `alt_modifier` settings. **Modifier Keys** are sticky. They will remain pressed until a non-modifier key is pressed, the modifier gets toggled off, or the keyboard gets hidden. @@ -150,9 +160,10 @@ Check [here](https://github.com/galister/wlx-overlay-s/wiki/Troubleshooting) for ### Mouse is not where it should be -Niri users: use on Niri 0.1.7 or later. - -X11 users might be dealing with a [Phantom Monitor](https://wiki.archlinux.org/title/Xrandr#Disabling_phantom_monitor). +X11 users: +- Might be dealing with a [Phantom Monitor](https://wiki.archlinux.org/title/Xrandr#Disabling_phantom_monitor). +- DPI scaling is not supported and will mess with the mouse. +- Upright screens are not supported and will mess with the mouse. Other desktops: The screens may have been selected in the wrong order, see [First Start](#first-start). @@ -168,17 +179,13 @@ echo 'capture_method: pw_fallback' > ~/.config/wlxoverlay/conf.d/pw_fallback.yam Without DMA-buf capture, capturing screens takes CPU power, so let's try and not show too many screens at the same time. -### Space-drag crashes SteamVR +### Modifiers get stuck, mouse clicks stop working on KDE Plasma -This has been idenfitied as an issue with SteamVR versions 2.5.5 and above (latest tested 2.7.2). One way to avoid the crash is by switching to the `temp-v1.27.5` branch of SteamVR (via beta selection) and selecting [Steam-Play-None](https://github.com/Scrumplex/Steam-Play-None) under the compatibility tab. - -### Modifiers get stuck in weird ways - -This is a rare issue that can make KDE Plasma not react to click or keys due to what seems to be a race condition with modifiers. Restarting the overlay fixes this. +We are not sure what causes this, but it only happens on KDE Plasma. Restarting the overlay fixes this. ### X11 limitations -- X11 capture can generally seem slow. This is because zero-copy GPU capture is not supported on the general X11 desktop. Consider trying Wayland or Picom. +- X11 capture can generally seem slow. This is because zero-copy GPU capture is not supported on the general X11 desktop. Consider trying Wayland. - DPI scaling is not supported and may cause the mouse to not follow the laser properly. -- Upright screens are not supported and can cause the mouse to act weirdly. +- Upright screens are not supported and can cause the mouse to not follow the laser properly. - Screen changes (connecting / disconnecting a display, resolution changes, etc) are not handled at runtime. Restart the overlay for these to take effect. diff --git a/dash-frontend/.editorconfig b/dash-frontend/.editorconfig new file mode 100644 index 0000000..f5834b7 --- /dev/null +++ b/dash-frontend/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*.rs] +indent_style = tab +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/dash-frontend/Cargo.toml b/dash-frontend/Cargo.toml new file mode 100644 index 0000000..5bd23d7 --- /dev/null +++ b/dash-frontend/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dash-frontend" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow.workspace = true +wgui = { path = "../wgui/" } +glam = { workspace = true, features = ["mint", "serde"] } +log = { workspace = true } +rust-embed = { workspace = true } +chrono = "0.4.42" +gio = "0.21.5" +gtk = "0.18.2" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +wlx-common = { path = "../wlx-common" } diff --git a/dash-frontend/assets/Quicksand-Bold.ttf.gz b/dash-frontend/assets/Quicksand-Bold.ttf.gz new file mode 100644 index 0000000..b0467a6 Binary files /dev/null and b/dash-frontend/assets/Quicksand-Bold.ttf.gz differ diff --git a/dash-frontend/assets/Quicksand-Light.ttf.gz b/dash-frontend/assets/Quicksand-Light.ttf.gz new file mode 100644 index 0000000..dbda8da Binary files /dev/null and b/dash-frontend/assets/Quicksand-Light.ttf.gz differ diff --git a/dash-frontend/assets/Quicksand-Regular.ttf.gz b/dash-frontend/assets/Quicksand-Regular.ttf.gz new file mode 100644 index 0000000..f31c5b6 Binary files /dev/null and b/dash-frontend/assets/Quicksand-Regular.ttf.gz differ diff --git a/dash-frontend/assets/dashboard/add.svg b/dash-frontend/assets/dashboard/add.svg new file mode 100644 index 0000000..1f80e29 --- /dev/null +++ b/dash-frontend/assets/dashboard/add.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/alphabetical.svg b/dash-frontend/assets/dashboard/alphabetical.svg new file mode 100644 index 0000000..0373dc5 --- /dev/null +++ b/dash-frontend/assets/dashboard/alphabetical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/apps.svg b/dash-frontend/assets/dashboard/apps.svg new file mode 100644 index 0000000..0ac73db --- /dev/null +++ b/dash-frontend/assets/dashboard/apps.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/back.svg b/dash-frontend/assets/dashboard/back.svg new file mode 100644 index 0000000..544a693 --- /dev/null +++ b/dash-frontend/assets/dashboard/back.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_10.svg b/dash-frontend/assets/dashboard/bat_10.svg new file mode 100644 index 0000000..e191e12 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_10.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_100.svg b/dash-frontend/assets/dashboard/bat_100.svg new file mode 100644 index 0000000..13ee847 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_100.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_20.svg b/dash-frontend/assets/dashboard/bat_20.svg new file mode 100644 index 0000000..d1f8897 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_20.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_30.svg b/dash-frontend/assets/dashboard/bat_30.svg new file mode 100644 index 0000000..ab76fe1 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_30.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_40.svg b/dash-frontend/assets/dashboard/bat_40.svg new file mode 100644 index 0000000..fa96a39 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_40.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_50.svg b/dash-frontend/assets/dashboard/bat_50.svg new file mode 100644 index 0000000..920078d --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_50.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_60.svg b/dash-frontend/assets/dashboard/bat_60.svg new file mode 100644 index 0000000..9820cf8 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_60.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_70.svg b/dash-frontend/assets/dashboard/bat_70.svg new file mode 100644 index 0000000..af3bc56 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_70.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_80.svg b/dash-frontend/assets/dashboard/bat_80.svg new file mode 100644 index 0000000..71213ae --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_80.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_90.svg b/dash-frontend/assets/dashboard/bat_90.svg new file mode 100644 index 0000000..3fb8e30 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_90.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_10.svg b/dash-frontend/assets/dashboard/bat_chr_10.svg new file mode 100644 index 0000000..ec0600d --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_10.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_100.svg b/dash-frontend/assets/dashboard/bat_chr_100.svg new file mode 100644 index 0000000..38d7778 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_100.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_20.svg b/dash-frontend/assets/dashboard/bat_chr_20.svg new file mode 100644 index 0000000..7454195 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_20.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_30.svg b/dash-frontend/assets/dashboard/bat_chr_30.svg new file mode 100644 index 0000000..a5a1161 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_30.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_40.svg b/dash-frontend/assets/dashboard/bat_chr_40.svg new file mode 100644 index 0000000..484d04c --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_40.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_50.svg b/dash-frontend/assets/dashboard/bat_chr_50.svg new file mode 100644 index 0000000..e30ac3b --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_50.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_60.svg b/dash-frontend/assets/dashboard/bat_chr_60.svg new file mode 100644 index 0000000..8d2c392 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_60.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_70.svg b/dash-frontend/assets/dashboard/bat_chr_70.svg new file mode 100644 index 0000000..7ebd603 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_70.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_80.svg b/dash-frontend/assets/dashboard/bat_chr_80.svg new file mode 100644 index 0000000..bb5f153 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_80.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/bat_chr_90.svg b/dash-frontend/assets/dashboard/bat_chr_90.svg new file mode 100644 index 0000000..1dfceb7 --- /dev/null +++ b/dash-frontend/assets/dashboard/bat_chr_90.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/binary.svg b/dash-frontend/assets/dashboard/binary.svg new file mode 100644 index 0000000..c5e9511 --- /dev/null +++ b/dash-frontend/assets/dashboard/binary.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/burger.svg b/dash-frontend/assets/dashboard/burger.svg new file mode 100644 index 0000000..35787d3 --- /dev/null +++ b/dash-frontend/assets/dashboard/burger.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/category_search.svg b/dash-frontend/assets/dashboard/category_search.svg new file mode 100644 index 0000000..b6d707b --- /dev/null +++ b/dash-frontend/assets/dashboard/category_search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/circle.svg b/dash-frontend/assets/dashboard/circle.svg new file mode 100644 index 0000000..e28c5e1 --- /dev/null +++ b/dash-frontend/assets/dashboard/circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/close.svg b/dash-frontend/assets/dashboard/close.svg new file mode 100644 index 0000000..0b749bb --- /dev/null +++ b/dash-frontend/assets/dashboard/close.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/cpu.svg b/dash-frontend/assets/dashboard/cpu.svg new file mode 100644 index 0000000..6af76cf --- /dev/null +++ b/dash-frontend/assets/dashboard/cpu.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/display.svg b/dash-frontend/assets/dashboard/display.svg new file mode 100644 index 0000000..f749bda --- /dev/null +++ b/dash-frontend/assets/dashboard/display.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/displayport.svg b/dash-frontend/assets/dashboard/displayport.svg new file mode 100644 index 0000000..6915266 --- /dev/null +++ b/dash-frontend/assets/dashboard/displayport.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/eye.svg b/dash-frontend/assets/dashboard/eye.svg new file mode 100644 index 0000000..6ad576c --- /dev/null +++ b/dash-frontend/assets/dashboard/eye.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/fix_floor.svg b/dash-frontend/assets/dashboard/fix_floor.svg new file mode 100644 index 0000000..28b18ee --- /dev/null +++ b/dash-frontend/assets/dashboard/fix_floor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/games.svg b/dash-frontend/assets/dashboard/games.svg new file mode 100644 index 0000000..ef5b6d1 --- /dev/null +++ b/dash-frontend/assets/dashboard/games.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/github.svg b/dash-frontend/assets/dashboard/github.svg new file mode 100644 index 0000000..887b902 --- /dev/null +++ b/dash-frontend/assets/dashboard/github.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/globe.svg b/dash-frontend/assets/dashboard/globe.svg new file mode 100644 index 0000000..bf429ab --- /dev/null +++ b/dash-frontend/assets/dashboard/globe.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/home.svg b/dash-frontend/assets/dashboard/home.svg new file mode 100644 index 0000000..08163c0 --- /dev/null +++ b/dash-frontend/assets/dashboard/home.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/knife.svg b/dash-frontend/assets/dashboard/knife.svg new file mode 100644 index 0000000..5f8d620 --- /dev/null +++ b/dash-frontend/assets/dashboard/knife.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/magic_wand.svg b/dash-frontend/assets/dashboard/magic_wand.svg new file mode 100644 index 0000000..e3471a6 --- /dev/null +++ b/dash-frontend/assets/dashboard/magic_wand.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/microphone.svg b/dash-frontend/assets/dashboard/microphone.svg new file mode 100644 index 0000000..9f6afba --- /dev/null +++ b/dash-frontend/assets/dashboard/microphone.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/minijack.svg b/dash-frontend/assets/dashboard/minijack.svg new file mode 100644 index 0000000..eec823f --- /dev/null +++ b/dash-frontend/assets/dashboard/minijack.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/monado.svg b/dash-frontend/assets/dashboard/monado.svg new file mode 100644 index 0000000..a50427d --- /dev/null +++ b/dash-frontend/assets/dashboard/monado.svg @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/dash-frontend/assets/dashboard/panorama.svg b/dash-frontend/assets/dashboard/panorama.svg new file mode 100644 index 0000000..d123ee2 --- /dev/null +++ b/dash-frontend/assets/dashboard/panorama.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/play.svg b/dash-frontend/assets/dashboard/play.svg new file mode 100644 index 0000000..a0f4ecb --- /dev/null +++ b/dash-frontend/assets/dashboard/play.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/power.svg b/dash-frontend/assets/dashboard/power.svg new file mode 100644 index 0000000..bbb5ef2 --- /dev/null +++ b/dash-frontend/assets/dashboard/power.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/recenter.svg b/dash-frontend/assets/dashboard/recenter.svg new file mode 100644 index 0000000..d5ee383 --- /dev/null +++ b/dash-frontend/assets/dashboard/recenter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/refresh.svg b/dash-frontend/assets/dashboard/refresh.svg new file mode 100644 index 0000000..0e633b1 --- /dev/null +++ b/dash-frontend/assets/dashboard/refresh.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/remove_circle.svg b/dash-frontend/assets/dashboard/remove_circle.svg new file mode 100644 index 0000000..a0eb15a --- /dev/null +++ b/dash-frontend/assets/dashboard/remove_circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/search.svg b/dash-frontend/assets/dashboard/search.svg new file mode 100644 index 0000000..482b8ac --- /dev/null +++ b/dash-frontend/assets/dashboard/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/settings.svg b/dash-frontend/assets/dashboard/settings.svg new file mode 100644 index 0000000..8659ae9 --- /dev/null +++ b/dash-frontend/assets/dashboard/settings.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/sleep.svg b/dash-frontend/assets/dashboard/sleep.svg new file mode 100644 index 0000000..cae7266 --- /dev/null +++ b/dash-frontend/assets/dashboard/sleep.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/terminal.svg b/dash-frontend/assets/dashboard/terminal.svg new file mode 100644 index 0000000..935681c --- /dev/null +++ b/dash-frontend/assets/dashboard/terminal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/usage.svg b/dash-frontend/assets/dashboard/usage.svg new file mode 100644 index 0000000..398b1ee --- /dev/null +++ b/dash-frontend/assets/dashboard/usage.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/volume.svg b/dash-frontend/assets/dashboard/volume.svg new file mode 100644 index 0000000..048d567 --- /dev/null +++ b/dash-frontend/assets/dashboard/volume.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/volume_off.svg b/dash-frontend/assets/dashboard/volume_off.svg new file mode 100644 index 0000000..f490fc5 --- /dev/null +++ b/dash-frontend/assets/dashboard/volume_off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/vr.svg b/dash-frontend/assets/dashboard/vr.svg new file mode 100644 index 0000000..7e399d3 --- /dev/null +++ b/dash-frontend/assets/dashboard/vr.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/dashboard/wayvr_dashboard.svg b/dash-frontend/assets/dashboard/wayvr_dashboard.svg new file mode 100644 index 0000000..06d51c6 --- /dev/null +++ b/dash-frontend/assets/dashboard/wayvr_dashboard.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/dash-frontend/assets/dashboard/wayvr_dashboard_mono.svg b/dash-frontend/assets/dashboard/wayvr_dashboard_mono.svg new file mode 100644 index 0000000..e2e7ad2 --- /dev/null +++ b/dash-frontend/assets/dashboard/wayvr_dashboard_mono.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + diff --git a/dash-frontend/assets/dashboard/window.svg b/dash-frontend/assets/dashboard/window.svg new file mode 100644 index 0000000..7de9ab7 --- /dev/null +++ b/dash-frontend/assets/dashboard/window.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/dashboard.xml b/dash-frontend/assets/gui/dashboard.xml new file mode 100644 index 0000000..aaab8af --- /dev/null +++ b/dash-frontend/assets/gui/dashboard.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ +
+ + +
+ + \ No newline at end of file diff --git a/dash-frontend/assets/gui/t_group_box.xml b/dash-frontend/assets/gui/t_group_box.xml new file mode 100644 index 0000000..d91b25e --- /dev/null +++ b/dash-frontend/assets/gui/t_group_box.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/t_menu_button.xml b/dash-frontend/assets/gui/t_menu_button.xml new file mode 100644 index 0000000..671189e --- /dev/null +++ b/dash-frontend/assets/gui/t_menu_button.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/t_separator.xml b/dash-frontend/assets/gui/t_separator.xml new file mode 100644 index 0000000..92a8285 --- /dev/null +++ b/dash-frontend/assets/gui/t_separator.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/apps.xml b/dash-frontend/assets/gui/tab/apps.xml new file mode 100644 index 0000000..fd8bf48 --- /dev/null +++ b/dash-frontend/assets/gui/tab/apps.xml @@ -0,0 +1,42 @@ + + + + + + + + +
+ + + + + + +
+
+ + \ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/games.xml b/dash-frontend/assets/gui/tab/games.xml new file mode 100644 index 0000000..4a1fde2 --- /dev/null +++ b/dash-frontend/assets/gui/tab/games.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/home.xml b/dash-frontend/assets/gui/tab/home.xml new file mode 100644 index 0000000..7a9d2ff --- /dev/null +++ b/dash-frontend/assets/gui/tab/home.xml @@ -0,0 +1,24 @@ + + + + +
+ +
+
+
\ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/monado.xml b/dash-frontend/assets/gui/tab/monado.xml new file mode 100644 index 0000000..729f356 --- /dev/null +++ b/dash-frontend/assets/gui/tab/monado.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/processes.xml b/dash-frontend/assets/gui/tab/processes.xml new file mode 100644 index 0000000..0330d48 --- /dev/null +++ b/dash-frontend/assets/gui/tab/processes.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/settings.xml b/dash-frontend/assets/gui/tab/settings.xml new file mode 100644 index 0000000..592c2b0 --- /dev/null +++ b/dash-frontend/assets/gui/tab/settings.xml @@ -0,0 +1,61 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+
+
\ No newline at end of file diff --git a/dash-frontend/assets/gui/tab/t_tab_title.xml b/dash-frontend/assets/gui/tab/t_tab_title.xml new file mode 100644 index 0000000..fc7e2b0 --- /dev/null +++ b/dash-frontend/assets/gui/tab/t_tab_title.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/theme.xml b/dash-frontend/assets/gui/theme.xml new file mode 100644 index 0000000..28f4e57 --- /dev/null +++ b/dash-frontend/assets/gui/theme.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/dash-frontend/assets/gui/view/app_launcher.xml b/dash-frontend/assets/gui/view/app_launcher.xml new file mode 100644 index 0000000..8b0b4bd --- /dev/null +++ b/dash-frontend/assets/gui/view/app_launcher.xml @@ -0,0 +1,40 @@ + + + + + + + + + +
+ + + +
+
+
+
+
\ No newline at end of file diff --git a/dash-frontend/assets/gui/view/audio_settings.xml b/dash-frontend/assets/gui/view/audio_settings.xml new file mode 100644 index 0000000..c792333 --- /dev/null +++ b/dash-frontend/assets/gui/view/audio_settings.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + +
+ +
+ + +
+
+
+
\ No newline at end of file diff --git a/dash-frontend/assets/gui/view/popup_window.xml b/dash-frontend/assets/gui/view/popup_window.xml new file mode 100644 index 0000000..8d2cf37 --- /dev/null +++ b/dash-frontend/assets/gui/view/popup_window.xml @@ -0,0 +1,45 @@ + + + + +
+ + + + + + + +
+ + + + +
+
+ + + + +
+
+
\ No newline at end of file diff --git a/dash-frontend/assets/lang/de.json b/dash-frontend/assets/lang/de.json new file mode 100644 index 0000000..ba4d28c --- /dev/null +++ b/dash-frontend/assets/lang/de.json @@ -0,0 +1,49 @@ +{ + "HOME_SCREEN": "Startbildschirm", + "MONADO_RUNTIME": "„Monado”-Laufzeitumgebung", + "APPLICATIONS": "Anwendungen", + "GAMES": "Spiele", + "SETTINGS": "Einstellungen", + "PROCESSES": "Prozesse", + "HELLO_USER": "Hallo, {USER}!", + "GENERAL_SETTINGS": "Allgemeine Einstellungen", + "APPLICATION_LAUNCHER": "Anwendung Launcher", + "APP_SETTINGS": { + "HIDE_USERNAME": "Benutzernamen ausblenden", + "OPAQUE_BACKGROUND": "Undurchsichtiger Hintergrund", + "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Standardmäßig in XWayland-Modus ausführen", + "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S Einstellungen", + "HEADSET_SETTINGS": "Headset-Einstellungen", + "BRIGHTNESS": "Helligkeit", + "WLX": { + "NOTIFICATIONS_ENABLED": "Benachrichtigungen aktiviert", + "NOTIFICATIONS_SOUND_ENABLED": "Benachrichtigungssound aktiviert", + "KEYBOARD_SOUND_ENABLED": "Tastaturgeräusch aktiviert", + "BLOCK_GAME_INPUT": "Spielsteuerung blockieren", + "SPACE_DRAG_MULTIPLIER": "Raum-Drag-Multiplikator", + "SPACE_DRAG_ROTATION_ENABLED": "Rotation im Space-Drag aktivieren", + "SHOW_SKYBOX": "Skybox anzeigen", + "ENABLE_PASSTHROUGH": "Passthrough aktivieren" + }, + "RESTART_SOFTWARE": "Software neu starten" + }, + "HELLO": "Hallo!", + "AUDIO": { + "VOLUME": "Lautstärke", + "SETTINGS": "Audioeinstellungen", + "AUTO_SWITCH_TO_VR_AUDIO": "Automatisch auf VR-Audio umschalten", + "SPEAKERS": "Lautsprecher", + "MICROPHONES": "Mikrofone", + "CARDS": "Karten", + "SELECT_AUDIO_CARD_PROFILE": "Wählen Sie das Audio-Kartenprofil", + "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "Keine VR-Lautsprecher gefunden. Schalten Sie diese manuell um.", + "NO_VR_MICROPHONE_SWITCH_MANUALLY": "Kein VR-Mikrofon gefunden. Schalten Sie es manuell um.", + "FAILED_TO_SWITCH_MICROPHONE": "Fehler beim Wechseln des Mikrofons", + "MICROPHONE_SET_SUCCESSFULLY": "Mikrofon erfolgreich eingestellt", + "SPEAKERS_SET_SUCCESSFULLY": "Lautsprecher erfolgreich eingestellt", + "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Gerät gefunden und initialisiert, aber nicht umgeschaltet" + }, + "ACTIONS": { + "RECENTER_PLAYSPACE": "Playspace neu zentrieren" + } +} \ No newline at end of file diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json new file mode 100644 index 0000000..b92964e --- /dev/null +++ b/dash-frontend/assets/lang/en.json @@ -0,0 +1,49 @@ +{ + "HOME_SCREEN": "Home", + "MONADO_RUNTIME": "„Monado” runtime", + "APPLICATIONS": "Applications", + "GAMES": "Games", + "SETTINGS": "Settings", + "PROCESSES": "Processes", + "HELLO_USER": "Hello, {USER}!", + "HELLO": "Hello!", + "GENERAL_SETTINGS": "General settings", + "APPLICATION_LAUNCHER": "Application launcher", + "APP_SETTINGS": { + "RESTART_SOFTWARE": "Restart software", + "HIDE_USERNAME": "Hide username", + "OPAQUE_BACKGROUND": "Opaque background", + "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Run in XWayland mode by default", + "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings", + "HEADSET_SETTINGS": "Headset settings", + "BRIGHTNESS": "Brightness", + "WLX": { + "NOTIFICATIONS_ENABLED": "Notifications enabled", + "NOTIFICATIONS_SOUND_ENABLED": "Notifications sound enabled", + "KEYBOARD_SOUND_ENABLED": "Keyboard sound enabled", + "BLOCK_GAME_INPUT": "Block game input", + "SPACE_DRAG_MULTIPLIER": "Space-drag multiplier", + "SPACE_DRAG_ROTATION_ENABLED": "Enable rotation in space-drag", + "SHOW_SKYBOX": "Show skybox", + "ENABLE_PASSTHROUGH": "Enable passthrough" + } + }, + "AUDIO": { + "SELECT_AUDIO_CARD_PROFILE": "Select audio card profile", + "SETTINGS": "Audio settings", + "VOLUME": "Volume", + "AUTO_SWITCH_TO_VR_AUDIO": "Auto-switch to VR audio", + "SPEAKERS": "Speakers", + "MICROPHONES": "Microphones", + "CARDS": "Cards", + "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "No VR speakers found. Switch them manually.", + "NO_VR_MICROPHONE_SWITCH_MANUALLY": "No VR microphone found. Switch it manually.", + "FAILED_TO_SWITCH_MICROPHONE": "Failed to switch microphone", + "MICROPHONE_SET_SUCCESSFULLY": "Microphone set successfully", + "SPEAKERS_SET_SUCCESSFULLY": "Speakers set successfully", + "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Device found and initialized, but not switched" + }, + "ACTIONS": { + "RECENTER_PLAYSPACE": "Re-center playspace" + } +} diff --git a/dash-frontend/assets/lang/es.json b/dash-frontend/assets/lang/es.json new file mode 100644 index 0000000..cb7869d --- /dev/null +++ b/dash-frontend/assets/lang/es.json @@ -0,0 +1,49 @@ +{ + "HOME_SCREEN": "Inicio", + "MONADO_RUNTIME": "„Monado” tiempo de ejecución", + "APPLICATIONS": "Aplicaciones", + "GAMES": "Juegos", + "SETTINGS": "Ajustes", + "PROCESSES": "Procesos", + "HELLO_USER": "¡Hola, {USER}!", + "GENERAL_SETTINGS": "Ajustes generales", + "APPLICATION_LAUNCHER": "Lanzador de aplicaciones", + "APP_SETTINGS": { + "HIDE_USERNAME": "Ocultar nombre de usuario", + "OPAQUE_BACKGROUND": "Fondo opaco", + "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Ejecutar en modo XWayland por defecto", + "WLX_OVERLAY_S_SETTINGS": "Configuración de WlxOverlay-S", + "HEADSET_SETTINGS": "Configuración del casco", + "BRIGHTNESS": "Brillo", + "WLX": { + "NOTIFICATIONS_ENABLED": "Notificaciones activadas", + "NOTIFICATIONS_SOUND_ENABLED": "Sonido de notificaciones activado", + "KEYBOARD_SOUND_ENABLED": "Sonido del teclado activado", + "BLOCK_GAME_INPUT": "Bloquear entrada del juego", + "SPACE_DRAG_MULTIPLIER": "Multiplicador de movimiento por arrastre", + "SPACE_DRAG_ROTATION_ENABLED": "Habilitar rotación en space-drag", + "SHOW_SKYBOX": "Mostrar cielo", + "ENABLE_PASSTHROUGH": "Habilitar Passthrough" + }, + "RESTART_SOFTWARE": "Reiniciar software" + }, + "HELLO": "¡Hola!", + "AUDIO": { + "VOLUME": "Volumen", + "SETTINGS": "Configuración de audio", + "AUTO_SWITCH_TO_VR_AUDIO": "Conmutar automáticamente al audio VR", + "SPEAKERS": "Altavoces", + "MICROPHONES": "Micrófonos", + "CARDS": "Tarjetas", + "SELECT_AUDIO_CARD_PROFILE": "Seleccionar perfil de tarjeta de audio", + "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "No se encontraron altavoces VR. Actívelos manualmente.", + "NO_VR_MICROPHONE_SWITCH_MANUALLY": "No se encontró micrófono VR. Actívelo manualmente.", + "FAILED_TO_SWITCH_MICROPHONE": "No se pudo cambiar el micrófono", + "MICROPHONE_SET_SUCCESSFULLY": "Micrófono configurado correctamente", + "SPEAKERS_SET_SUCCESSFULLY": "Altavoces configurados correctamente", + "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Dispositivo encontrado e inicializado, pero no cambiado" + }, + "ACTIONS": { + "RECENTER_PLAYSPACE": "Re-centrar espacio de juego" + } +} \ No newline at end of file diff --git a/dash-frontend/assets/lang/ja.json b/dash-frontend/assets/lang/ja.json new file mode 100644 index 0000000..e8c59fb --- /dev/null +++ b/dash-frontend/assets/lang/ja.json @@ -0,0 +1,49 @@ +{ + "HOME_SCREEN": "ホーム", + "MONADO_RUNTIME": "「Monado」ランタイム", + "APPLICATIONS": "アプリケーション", + "GAMES": "ゲーム", + "SETTINGS": "設定", + "PROCESSES": "プロセス", + "HELLO_USER": "こんにちは、{USER}!", + "GENERAL_SETTINGS": "全般設定", + "APPLICATION_LAUNCHER": "アプリケーションランチャー", + "APP_SETTINGS": { + "HIDE_USERNAME": "ユーザー名を表示しない", + "OPAQUE_BACKGROUND": "不透明な背景", + "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "XWaylandモードでデフォルトで実行する", + "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-Sの設定", + "HEADSET_SETTINGS": "ヘッドセット設定", + "BRIGHTNESS": "明るさ", + "WLX": { + "NOTIFICATIONS_ENABLED": "通知が有効", + "NOTIFICATIONS_SOUND_ENABLED": "通知音を有効にする", + "KEYBOARD_SOUND_ENABLED": "キーボード音を有効にする", + "BLOCK_GAME_INPUT": "ゲーム入力をブロック", + "SPACE_DRAG_MULTIPLIER": "スペースドラッグ乗数", + "SPACE_DRAG_ROTATION_ENABLED": "スペースドラッグでの回転を有効にする", + "SHOW_SKYBOX": "スカイボックスを表示", + "ENABLE_PASSTHROUGH": "Passthroughを有効にする" + }, + "RESTART_SOFTWARE": "ソフトウェアを再起動" + }, + "HELLO": "こんにちは!", + "AUDIO": { + "VOLUME": "音量", + "SETTINGS": "オーディオ設定", + "AUTO_SWITCH_TO_VR_AUDIO": "VRオーディオに自動切り替え", + "SPEAKERS": "スピーカー", + "MICROPHONES": "マイク", + "CARDS": "カード", + "SELECT_AUDIO_CARD_PROFILE": "オーディオカードプロファイルを選択", + "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "VRスピーカーが見つかりませんでした。手動で切り替えてください。", + "NO_VR_MICROPHONE_SWITCH_MANUALLY": "VRマイクが見つかりませんでした。手動で切り替えてください。", + "FAILED_TO_SWITCH_MICROPHONE": "マイクの切り替えに失敗しました", + "MICROPHONE_SET_SUCCESSFULLY": "マイクの設定が完了しました", + "SPEAKERS_SET_SUCCESSFULLY": "スピーカーを設定しました", + "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "デバイスが見つかり、初期化されましたが、切り替えられていません" + }, + "ACTIONS": { + "RECENTER_PLAYSPACE": "プレイスペースを再中央" + } +} \ No newline at end of file diff --git a/dash-frontend/assets/lang/pl.json b/dash-frontend/assets/lang/pl.json new file mode 100644 index 0000000..f2896e2 --- /dev/null +++ b/dash-frontend/assets/lang/pl.json @@ -0,0 +1,49 @@ +{ + "HOME_SCREEN": "Ekran główny", + "MONADO_RUNTIME": "„Monado” środowisko uruchomieniowe", + "APPLICATIONS": "Aplikacje", + "GAMES": "Gry", + "SETTINGS": "Ustawienia", + "PROCESSES": "Procesy", + "HELLO_USER": "Witaj, {USER}!", + "GENERAL_SETTINGS": "Ustawienia ogólne", + "APPLICATION_LAUNCHER": "Uruchamiacz aplikacji", + "APP_SETTINGS": { + "HIDE_USERNAME": "Ukryj nazwę użytkownika", + "OPAQUE_BACKGROUND": "Nieprzezroczyste tło", + "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Uruchom domyślnie w trybie XWayland", + "WLX_OVERLAY_S_SETTINGS": "Ustawienia wlx-overlay-s", + "HEADSET_SETTINGS": "Ustawienia HMD", + "BRIGHTNESS": "Jasność", + "WLX": { + "NOTIFICATIONS_ENABLED": "Powiadomienia", + "NOTIFICATIONS_SOUND_ENABLED": "Dźwięk powiadomień", + "KEYBOARD_SOUND_ENABLED": "Dźwięki klawiatury", + "BLOCK_GAME_INPUT": "Zablokuj sterowanie grą podczas używania Wlx", + "SPACE_DRAG_MULTIPLIER": "Mnożnik space-drag", + "SPACE_DRAG_ROTATION_ENABLED": "Włącz rotację w space-drag", + "SHOW_SKYBOX": "Pokaż skybox", + "ENABLE_PASSTHROUGH": "Włącz passthrough" + }, + "RESTART_SOFTWARE": "Uruchom ponownie oprogramowanie" + }, + "HELLO": "Witaj!", + "AUDIO": { + "VOLUME": "Głośność", + "SETTINGS": "Ustawienia dźwięku", + "AUTO_SWITCH_TO_VR_AUDIO": "Automatyczne przełączanie na dźwięk VR", + "SPEAKERS": "Głośniki", + "MICROPHONES": "Mikrofony", + "CARDS": "Karty", + "SELECT_AUDIO_CARD_PROFILE": "Wybierz profil karty dźwiękowej", + "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "Brak głośników VR. Włącz je ręcznie.", + "NO_VR_MICROPHONE_SWITCH_MANUALLY": "Brak mikrofonu VR. Włącz go ręcznie.", + "FAILED_TO_SWITCH_MICROPHONE": "Nie udało się przełączyć mikrofon", + "MICROPHONE_SET_SUCCESSFULLY": "Mikrofon ustawiono pomyślnie", + "SPEAKERS_SET_SUCCESSFULLY": "Głośniki ustawiono pomyślnie", + "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Urządzenie znalezione i zainicjalizowane, ale nie przełączone" + }, + "ACTIONS": { + "RECENTER_PLAYSPACE": "Wycentruj przestrzeń" + } +} \ No newline at end of file diff --git a/dash-frontend/rustfmt.toml b/dash-frontend/rustfmt.toml new file mode 100644 index 0000000..bfdeee4 --- /dev/null +++ b/dash-frontend/rustfmt.toml @@ -0,0 +1,3 @@ +tab_spaces = 2 +hard_tabs = true +max_width = 120 diff --git a/dash-frontend/src/assets.rs b/dash-frontend/src/assets.rs new file mode 100644 index 0000000..cbc6b5e --- /dev/null +++ b/dash-frontend/src/assets.rs @@ -0,0 +1,12 @@ +#[derive(rust_embed::Embed)] +#[folder = "assets/"] +pub struct Asset; + +impl wgui::assets::AssetProvider for Asset { + fn load_from_path(&mut self, path: &str) -> anyhow::Result> { + match Asset::get(path) { + Some(data) => Ok(data.data.to_vec()), + None => anyhow::bail!("embedded file {} not found", path), + } + } +} diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs new file mode 100644 index 0000000..f1397c7 --- /dev/null +++ b/dash-frontend/src/frontend.rs @@ -0,0 +1,434 @@ +use std::{cell::RefCell, path::PathBuf, rc::Rc}; + +use chrono::Timelike; +use glam::Vec2; +use wgui::{ + assets::{AssetPath, AssetProvider}, + components::button::ComponentButton, + font_config::WguiFontConfig, + globals::WguiGlobals, + i18n::Translation, + layout::{LayoutParams, RcLayout, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + widget::{label::WidgetLabel, rectangle::WidgetRectangle}, + windowing::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement}, +}; +use wlx_common::timestep::Timestep; + +use crate::{ + assets, settings, + tab::{ + Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, + settings::TabSettings, + }, + task::Tasks, + util::{ + popup_manager::{MountPopupParams, PopupManager, PopupManagerParams}, + toast_manager::ToastManager, + }, + views, +}; + +pub struct FrontendWidgets { + pub id_label_time: WidgetID, + pub id_rect_content: WidgetID, +} + +pub type FrontendTasks = Tasks; + +pub struct Frontend { + pub layout: RcLayout, + globals: WguiGlobals, + + pub settings: Box, + + #[allow(dead_code)] + state: ParserState, + + current_tab: Option>, + + pub tasks: FrontendTasks, + + ticks: u32, + + widgets: FrontendWidgets, + popup_manager: PopupManager, + toast_manager: ToastManager, + timestep: Timestep, + + window_audio_settings: WguiWindow, + view_audio_settings: Option, +} + +pub struct InitParams { + pub settings: Box, +} + +pub type RcFrontend = Rc>; + +#[derive(Clone)] +pub enum FrontendTask { + SetTab(TabType), + RefreshClock, + RefreshBackground, + MountPopup(MountPopupParams), + RefreshPopupManager, + ShowAudioSettings, + UpdateAudioSettingsView, + RecenterPlayspace, + PushToast(Translation), +} + +impl Frontend { + pub fn new(params: InitParams) -> anyhow::Result<(RcFrontend, RcLayout)> { + let mut assets = Box::new(assets::Asset {}); + + let font_binary_bold = assets.load_from_path_gzip("Quicksand-Bold.ttf.gz")?; + let font_binary_regular = assets.load_from_path_gzip("Quicksand-Regular.ttf.gz")?; + let font_binary_light = assets.load_from_path_gzip("Quicksand-Light.ttf.gz")?; + + let globals = WguiGlobals::new( + assets, + wgui::globals::Defaults::default(), + &WguiFontConfig { + binaries: vec![&font_binary_regular, &font_binary_bold, &font_binary_light], + family_name_sans_serif: "Quicksand", + family_name_serif: "Quicksand", + family_name_monospace: "", + }, + PathBuf::new(), //FIXME: pass from somewhere else + )?; + + let (layout, state) = wgui::parser::new_layout_from_assets( + &ParseDocumentParams { + globals: globals.clone(), + path: AssetPath::BuiltIn("gui/dashboard.xml"), + extra: Default::default(), + }, + &LayoutParams { resize_to_parent: true }, + )?; + + let id_popup_manager = state.get_widget_id("popup_manager")?; + let popup_manager = PopupManager::new(PopupManagerParams { + parent_id: id_popup_manager, + }); + + let toast_manager = ToastManager::new(); + + let rc_layout = layout.as_rc(); + + let tasks = FrontendTasks::new(); + tasks.push(FrontendTask::SetTab(TabType::Home)); + + let id_label_time = state.get_widget_id("label_time")?; + let id_rect_content = state.get_widget_id("rect_content")?; + + let mut timestep = Timestep::new(); + timestep.set_tps(30.0); // 30 ticks per second + + let frontend = Self { + layout: rc_layout.clone(), + state, + current_tab: None, + globals, + tasks, + ticks: 0, + widgets: FrontendWidgets { + id_label_time, + id_rect_content, + }, + timestep, + settings: params.settings, + popup_manager, + toast_manager, + window_audio_settings: WguiWindow::default(), + view_audio_settings: None, + }; + + // init some things first + frontend.update_background()?; + frontend.update_time()?; + + let res = Rc::new(RefCell::new(frontend)); + + Frontend::register_widgets(&res)?; + + Ok((res, rc_layout)) + } + + pub fn update(&mut self, rc_this: &RcFrontend, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> { + let mut tasks = self.tasks.drain(); + + while let Some(task) = tasks.pop_front() { + self.process_task(rc_this, task)?; + } + + self.tick(width, height, timestep_alpha)?; + self.ticks += 1; + + Ok(()) + } + + fn tick(&mut self, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> { + // fixme: timer events instead of this thing + if self.ticks.is_multiple_of(1000) { + self.update_time()?; + } + + { + let mut layout = self.layout.borrow_mut(); + + // always 30 times per second + while self.timestep.on_tick() { + self.toast_manager.tick(&self.globals, &mut layout)?; + } + + layout.update(Vec2::new(width, height), timestep_alpha)?; + } + + Ok(()) + } + + fn update_time(&self) -> anyhow::Result<()> { + let mut layout = self.layout.borrow_mut(); + let mut c = layout.start_common(); + let mut common = c.common(); + + { + let Some(mut label) = common.state.widgets.get_as::(self.widgets.id_label_time) else { + anyhow::bail!(""); + }; + + let now = chrono::Local::now(); + let hours = now.hour(); + let minutes = now.minute(); + + let text: String = if !self.settings.get().general.am_pm_clock { + format!("{hours:02}:{minutes:02}") + } else { + let hours_ampm = (hours + 11) % 12 + 1; + let suffix = if hours >= 12 { "PM" } else { "AM" }; + format!("{hours_ampm:02}:{minutes:02} {suffix}") + }; + + label.set_text(&mut common, Translation::from_raw_text(&text)); + } + + c.finish()?; + Ok(()) + } + + fn mount_popup(&mut self, params: MountPopupParams) -> anyhow::Result<()> { + let mut layout = self.layout.borrow_mut(); + self + .popup_manager + .mount_popup(self.globals.clone(), &mut layout, self.tasks.clone(), params)?; + Ok(()) + } + + fn refresh_popup_manager(&mut self) -> anyhow::Result<()> { + let mut layout = self.layout.borrow_mut(); + let mut c = layout.start_common(); + self.popup_manager.refresh(c.common().alterables); + c.finish()?; + Ok(()) + } + + fn update_background(&self) -> anyhow::Result<()> { + let layout = self.layout.borrow_mut(); + + let Some(mut rect) = layout + .state + .widgets + .get_as::(self.widgets.id_rect_content) + else { + anyhow::bail!(""); + }; + + let (alpha1, alpha2) = if !self.settings.get().general.opaque_background { + (0.8666, 0.9333) + } else { + (1.0, 1.0) + }; + + rect.params.color.a = alpha1; + rect.params.color2.a = alpha2; + + Ok(()) + } + + pub fn get_layout(&self) -> &RcLayout { + &self.layout + } + + fn process_task(&mut self, rc_this: &RcFrontend, task: FrontendTask) -> anyhow::Result<()> { + match task { + FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?, + FrontendTask::RefreshClock => self.update_time()?, + FrontendTask::RefreshBackground => self.update_background()?, + FrontendTask::MountPopup(params) => self.mount_popup(params)?, + FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?, + FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?, + FrontendTask::UpdateAudioSettingsView => self.action_update_audio_settings()?, + FrontendTask::RecenterPlayspace => self.action_recenter_playspace()?, + FrontendTask::PushToast(content) => self.toast_manager.push(content), + }; + Ok(()) + } + + fn set_tab(&mut self, tab_type: TabType, rc_this: &RcFrontend) -> anyhow::Result<()> { + log::info!("Setting tab to {tab_type:?}"); + let mut layout = self.layout.borrow_mut(); + let widget_content = self.state.fetch_widget(&layout.state, "content")?; + layout.remove_children(widget_content.id); + + let tab_params = TabParams { + globals: &self.globals, + layout: &mut layout, + parent_id: widget_content.id, + frontend: rc_this, + //frontend_widgets: &self.widgets, + settings: self.settings.get_mut(), + }; + + let tab: Box = match tab_type { + TabType::Home => Box::new(TabHome::new(tab_params)?), + TabType::Apps => Box::new(TabApps::new(tab_params)?), + TabType::Games => Box::new(TabGames::new(tab_params)?), + TabType::Monado => Box::new(TabMonado::new(tab_params)?), + TabType::Processes => Box::new(TabProcesses::new(tab_params)?), + TabType::Settings => Box::new(TabSettings::new(tab_params)?), + }; + + self.current_tab = Some(tab); + + Ok(()) + } + + pub fn register_button_task(this_rc: RcFrontend, btn: &Rc, task: FrontendTask) { + btn.on_click({ + Box::new(move |_common, _evt| { + this_rc.borrow_mut().tasks.push(task.clone()); + Ok(()) + }) + }); + } + + fn register_widgets(rc_this: &RcFrontend) -> anyhow::Result<()> { + let this = rc_this.borrow_mut(); + + // ################################ + // SIDE BUTTONS + // ################################ + + // "Home" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_home")?, + FrontendTask::SetTab(TabType::Home), + ); + + // "Apps" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_apps")?, + FrontendTask::SetTab(TabType::Apps), + ); + + // "Games" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_games")?, + FrontendTask::SetTab(TabType::Games), + ); + + // "Monado side button" + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_monado")?, + FrontendTask::SetTab(TabType::Monado), + ); + + // "Processes" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_processes")?, + FrontendTask::SetTab(TabType::Processes), + ); + + // "Settings" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_settings")?, + FrontendTask::SetTab(TabType::Settings), + ); + + // ################################ + // BOTTOM BAR BUTTONS + // ################################ + + // "Audio" bottom bar button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_audio")?, + FrontendTask::ShowAudioSettings, + ); + + // "Recenter playspace" bottom bar button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_recenter")?, + FrontendTask::RecenterPlayspace, + ); + + Ok(()) + } + + fn action_show_audio_settings(&mut self) -> anyhow::Result<()> { + let mut layout = self.layout.borrow_mut(); + + self.window_audio_settings.open(&mut WguiWindowParams { + globals: self.globals.clone(), + position: Vec2::new(64.0, 64.0), + layout: &mut layout, + title: Translation::from_translation_key("AUDIO.SETTINGS"), + extra: WguiWindowParamsExtra { + fixed_width: Some(400.0), + placement: WguiWindowPlacement::BottomLeft, + ..Default::default() + }, + })?; + + let content = self.window_audio_settings.get_content(); + + self.view_audio_settings = Some(views::audio_settings::View::new(views::audio_settings::Params { + globals: self.globals.clone(), + frontend_tasks: self.tasks.clone(), + layout: &mut layout, + parent_id: content.id, + on_update: { + let tasks = self.tasks.clone(); + Rc::new(move || { + tasks.push(FrontendTask::UpdateAudioSettingsView); + }) + }, + })?); + Ok(()) + } + + fn action_update_audio_settings(&mut self) -> anyhow::Result<()> { + let Some(view) = &mut self.view_audio_settings else { + return Ok(()); + }; + + let mut layout = self.layout.borrow_mut(); + view.update(&mut layout)?; + + Ok(()) + } + + fn action_recenter_playspace(&mut self) -> anyhow::Result<()> { + log::info!("todo"); + Ok(()) + } +} diff --git a/dash-frontend/src/lib.rs b/dash-frontend/src/lib.rs new file mode 100644 index 0000000..e930036 --- /dev/null +++ b/dash-frontend/src/lib.rs @@ -0,0 +1,8 @@ +mod assets; +pub mod frontend; +pub mod settings; +mod tab; +mod task; +mod util; +mod various; +mod views; diff --git a/dash-frontend/src/settings.rs b/dash-frontend/src/settings.rs new file mode 100644 index 0000000..d4e2402 --- /dev/null +++ b/dash-frontend/src/settings.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Default, Serialize, Deserialize)] +pub struct HomeScreen { + pub hide_username: bool, +} + +#[derive(Default, Serialize, Deserialize)] +pub struct General { + pub am_pm_clock: bool, + pub opaque_background: bool, +} + +#[derive(Default, Serialize, Deserialize)] +pub struct Tweaks { + pub xwayland_by_default: bool, +} + +#[derive(Default, Serialize, Deserialize)] +pub struct Settings { + pub home_screen: HomeScreen, + pub general: General, + pub tweaks: Tweaks, +} + +impl Settings { + pub fn save(&self) -> String { + serde_json::to_string_pretty(&self).unwrap() /* want panic */ + } + + pub fn load(input: &str) -> anyhow::Result { + Ok(serde_json::from_str::(input)?) + } +} + +pub trait SettingsIO { + fn get_mut(&mut self) -> &mut Settings; + fn get(&self) -> &Settings; + fn save_to_disk(&mut self); + fn read_from_disk(&mut self); + fn mark_as_dirty(&mut self); +} diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs new file mode 100644 index 0000000..ee9ecc3 --- /dev/null +++ b/dash-frontend/src/tab/apps.rs @@ -0,0 +1,181 @@ +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +use wgui::{ + assets::AssetPath, + components::button::{ButtonClickCallback, ComponentButton}, + globals::WguiGlobals, + i18n::Translation, + layout::WidgetPair, + parser::{Fetchable, ParseDocumentParams, ParserState}, +}; + +use crate::{ + frontend::{FrontendTask, RcFrontend}, + tab::{Tab, TabParams, TabType}, + util::{ + self, + desktop_finder::DesktopEntry, + popup_manager::{MountPopupParams, PopupHandle}, + }, + views::{self, app_launcher}, +}; + +struct State { + launcher: Option<(PopupHandle, views::app_launcher::View)>, +} + +pub struct TabApps { + #[allow(dead_code)] + pub parser_state: ParserState, + + #[allow(dead_code)] + state: Rc>, + + #[allow(dead_code)] + entries: Vec, + #[allow(dead_code)] + app_list: AppList, +} + +impl Tab for TabApps { + fn get_type(&self) -> TabType { + TabType::Apps + } +} + +#[derive(Default)] +struct AppList { + //data: Vec, +} + +// called after the user clicks any desktop entry +fn on_app_click( + frontend: RcFrontend, + globals: WguiGlobals, + entry: DesktopEntry, + state: Rc>, +) -> ButtonClickCallback { + Box::new(move |_common, _evt| { + frontend + .borrow_mut() + .tasks + .push(FrontendTask::MountPopup(MountPopupParams { + title: Translation::from_raw_text(&entry.app_name), + on_content: { + let state = state.clone(); + let entry = entry.clone(); + let globals = globals.clone(); + Rc::new(move |data| { + let view = app_launcher::View::new(app_launcher::Params { + entry: entry.clone(), + globals: globals.clone(), + layout: data.layout, + parent_id: data.id_content, + })?; + + state.borrow_mut().launcher = Some((data.handle, view)); + Ok(()) + }) + }, + })); + Ok(()) + }) +} + +impl TabApps { + pub fn new(mut tab_params: TabParams) -> anyhow::Result { + let doc_params = &ParseDocumentParams { + globals: tab_params.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/apps.xml"), + extra: Default::default(), + }; + + gtk::init()?; + let entries = util::desktop_finder::find_entries()?; + + let frontend = tab_params.frontend.clone(); + let globals = tab_params.globals.clone(); + + let state = Rc::new(RefCell::new(State { launcher: None })); + + let mut parser_state = wgui::parser::parse_from_assets(doc_params, tab_params.layout, tab_params.parent_id)?; + let app_list_parent = parser_state.fetch_widget(&tab_params.layout.state, "app_list_parent")?; + let mut app_list = AppList::default(); + app_list.mount_entries( + &entries, + &mut parser_state, + doc_params, + &mut tab_params, + &app_list_parent, + |button, entry| { + // Set up the click handler for the app button + button.on_click(on_app_click( + frontend.clone(), + globals.clone(), + entry.clone(), + state.clone(), + )); + }, + )?; + + Ok(Self { + app_list, + parser_state, + entries, + state, + }) + } +} + +impl AppList { + fn mount_entry( + &mut self, + parser_state: &mut ParserState, + doc_params: &ParseDocumentParams, + params: &mut TabParams, + list_parent: &WidgetPair, + entry: &DesktopEntry, + ) -> anyhow::Result> { + let mut template_params = HashMap::new(); + + // entry icon + template_params.insert( + Rc::from("src_ext"), + entry + .icon_path + .as_ref() + .map_or_else(|| Rc::from(""), |icon_path| Rc::from(icon_path.as_str())), + ); + + // entry fallback (question mark) icon + template_params.insert( + Rc::from("src"), + if entry.icon_path.is_none() { + Rc::from("dashboard/terminal.svg") + } else { + Rc::from("") + }, + ); + + template_params.insert(Rc::from("name"), Rc::from(entry.app_name.as_str())); + + let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?; + data.fetch_component_as::("button") + } + + fn mount_entries( + &mut self, + entries: &[DesktopEntry], + parser_state: &mut ParserState, + doc_params: &ParseDocumentParams, + params: &mut TabParams, + list_parent: &WidgetPair, + on_button: impl Fn(Rc, &DesktopEntry), + ) -> anyhow::Result<()> { + for entry in entries { + let button = self.mount_entry(parser_state, doc_params, params, list_parent, entry)?; + on_button(button, entry); + } + Ok(()) + } +} diff --git a/dash-frontend/src/tab/games.rs b/dash-frontend/src/tab/games.rs new file mode 100644 index 0000000..1a304c7 --- /dev/null +++ b/dash-frontend/src/tab/games.rs @@ -0,0 +1,33 @@ +use wgui::{ + assets::AssetPath, + parser::{ParseDocumentParams, ParserState}, +}; + +use crate::tab::{Tab, TabParams, TabType}; + +pub struct TabGames { + #[allow(dead_code)] + pub state: ParserState, +} + +impl Tab for TabGames { + fn get_type(&self) -> TabType { + TabType::Games + } +} + +impl TabGames { + pub fn new(params: TabParams) -> anyhow::Result { + let state = wgui::parser::parse_from_assets( + &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/games.xml"), + extra: Default::default(), + }, + params.layout, + params.parent_id, + )?; + + Ok(Self { state }) + } +} diff --git a/dash-frontend/src/tab/home.rs b/dash-frontend/src/tab/home.rs new file mode 100644 index 0000000..ffb4084 --- /dev/null +++ b/dash-frontend/src/tab/home.rs @@ -0,0 +1,82 @@ +use wgui::{ + assets::AssetPath, + components::button::ComponentButton, + event::CallbackDataCommon, + i18n::Translation, + layout::Widget, + parser::{Fetchable, ParseDocumentParams, ParserState}, + widget::label::WidgetLabel, +}; + +use crate::{ + frontend::{Frontend, FrontendTask}, + settings, + tab::{Tab, TabParams, TabType}, + various, +}; + +pub struct TabHome { + #[allow(dead_code)] + pub state: ParserState, +} + +impl Tab for TabHome { + fn get_type(&self) -> TabType { + TabType::Home + } +} + +fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, settings: &settings::Settings) { + let mut username = various::get_username(); + // first character as uppercase + if let Some(first) = username.chars().next() { + let first = first.to_uppercase().to_string(); + username.replace_range(0..1, &first); + } + + let translated = if !settings.home_screen.hide_username { + common.i18n().translate_and_replace("HELLO_USER", ("{USER}", &username)) + } else { + common.i18n().translate("HELLO").to_string() + }; + + let mut label_hello = label_hello.get_as_mut::().unwrap(); + label_hello.set_text(common, Translation::from_raw_text(&translated)); +} + +impl TabHome { + pub fn new(params: TabParams) -> anyhow::Result { + let state = wgui::parser::parse_from_assets( + &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/home.xml"), + extra: Default::default(), + }, + params.layout, + params.parent_id, + )?; + + let mut c = params.layout.start_common(); + let widget_label = state.fetch_widget(&c.layout.state, "label_hello")?.widget; + configure_label_hello(&mut c.common(), widget_label, params.settings); + + let btn_apps = state.fetch_component_as::("btn_apps")?; + let btn_games = state.fetch_component_as::("btn_games")?; + let btn_monado = state.fetch_component_as::("btn_monado")?; + let btn_processes = state.fetch_component_as::("btn_processes")?; + let btn_settings = state.fetch_component_as::("btn_settings")?; + + let frontend = params.frontend; + Frontend::register_button_task(frontend.clone(), &btn_apps, FrontendTask::SetTab(TabType::Apps)); + Frontend::register_button_task(frontend.clone(), &btn_games, FrontendTask::SetTab(TabType::Games)); + Frontend::register_button_task(frontend.clone(), &btn_monado, FrontendTask::SetTab(TabType::Monado)); + Frontend::register_button_task( + frontend.clone(), + &btn_processes, + FrontendTask::SetTab(TabType::Processes), + ); + Frontend::register_button_task(frontend.clone(), &btn_settings, FrontendTask::SetTab(TabType::Settings)); + + Ok(Self { state }) + } +} diff --git a/dash-frontend/src/tab/mod.rs b/dash-frontend/src/tab/mod.rs new file mode 100644 index 0000000..15a2f75 --- /dev/null +++ b/dash-frontend/src/tab/mod.rs @@ -0,0 +1,36 @@ +use wgui::{ + globals::WguiGlobals, + layout::{Layout, WidgetID}, +}; + +use crate::frontend::RcFrontend; + +pub mod apps; +pub mod games; +pub mod home; +pub mod monado; +pub mod processes; +pub mod settings; + +#[derive(Clone, Copy, Debug)] +pub enum TabType { + Home, + Apps, + Games, + Monado, + Processes, + Settings, +} + +pub struct TabParams<'a> { + pub globals: &'a WguiGlobals, + pub layout: &'a mut Layout, + pub parent_id: WidgetID, + pub frontend: &'a RcFrontend, + pub settings: &'a mut crate::settings::Settings, +} + +pub trait Tab { + #[allow(dead_code)] + fn get_type(&self) -> TabType; +} diff --git a/dash-frontend/src/tab/monado.rs b/dash-frontend/src/tab/monado.rs new file mode 100644 index 0000000..4130ac7 --- /dev/null +++ b/dash-frontend/src/tab/monado.rs @@ -0,0 +1,33 @@ +use wgui::{ + assets::AssetPath, + parser::{ParseDocumentParams, ParserState}, +}; + +use crate::tab::{Tab, TabParams, TabType}; + +pub struct TabMonado { + #[allow(dead_code)] + pub state: ParserState, +} + +impl Tab for TabMonado { + fn get_type(&self) -> TabType { + TabType::Games + } +} + +impl TabMonado { + pub fn new(params: TabParams) -> anyhow::Result { + let state = wgui::parser::parse_from_assets( + &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/monado.xml"), + extra: Default::default(), + }, + params.layout, + params.parent_id, + )?; + + Ok(Self { state }) + } +} diff --git a/dash-frontend/src/tab/processes.rs b/dash-frontend/src/tab/processes.rs new file mode 100644 index 0000000..a19bf28 --- /dev/null +++ b/dash-frontend/src/tab/processes.rs @@ -0,0 +1,33 @@ +use wgui::{ + assets::AssetPath, + parser::{ParseDocumentParams, ParserState}, +}; + +use crate::tab::{Tab, TabParams, TabType}; + +pub struct TabProcesses { + #[allow(dead_code)] + pub state: ParserState, +} + +impl Tab for TabProcesses { + fn get_type(&self) -> TabType { + TabType::Games + } +} + +impl TabProcesses { + pub fn new(params: TabParams) -> anyhow::Result { + let state = wgui::parser::parse_from_assets( + &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/processes.xml"), + extra: Default::default(), + }, + params.layout, + params.parent_id, + )?; + + Ok(Self { state }) + } +} diff --git a/dash-frontend/src/tab/settings.rs b/dash-frontend/src/tab/settings.rs new file mode 100644 index 0000000..3189e24 --- /dev/null +++ b/dash-frontend/src/tab/settings.rs @@ -0,0 +1,103 @@ +use std::rc::Rc; + +use wgui::{ + assets::AssetPath, + components::checkbox::ComponentCheckbox, + parser::{Fetchable, ParseDocumentParams, ParserState}, +}; + +use crate::{ + frontend::{Frontend, FrontendTask}, + settings, + tab::{Tab, TabParams, TabType}, +}; + +pub struct TabSettings { + #[allow(dead_code)] + pub state: ParserState, +} + +impl Tab for TabSettings { + fn get_type(&self) -> TabType { + TabType::Settings + } +} + +fn init_setting_checkbox( + params: &mut TabParams, + checkbox: Rc, + fetch_callback: fn(&mut settings::Settings) -> &mut bool, + change_callback: Option, +) -> anyhow::Result<()> { + let mut c = params.layout.start_common(); + + checkbox.set_checked(&mut c.common(), *fetch_callback(params.settings)); + let rc_frontend = params.frontend.clone(); + checkbox.on_toggle(Box::new(move |_common, e| { + let mut frontend = rc_frontend.borrow_mut(); + *fetch_callback(frontend.settings.get_mut()) = e.checked; + + if let Some(change_callback) = &change_callback { + change_callback(&mut frontend, e.checked); + } + + frontend.settings.mark_as_dirty(); + + Ok(()) + })); + + c.finish()?; + Ok(()) +} + +impl TabSettings { + pub fn new(mut params: TabParams) -> anyhow::Result { + let state = wgui::parser::parse_from_assets( + &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/settings.xml"), + extra: Default::default(), + }, + params.layout, + params.parent_id, + )?; + + init_setting_checkbox( + &mut params, + state.data.fetch_component_as::("cb_hide_username")?, + |settings| &mut settings.home_screen.hide_username, + None, + )?; + + init_setting_checkbox( + &mut params, + state.data.fetch_component_as::("cb_am_pm_clock")?, + |settings| &mut settings.general.am_pm_clock, + Some(|frontend, _| { + frontend.tasks.push(FrontendTask::RefreshClock); + }), + )?; + + init_setting_checkbox( + &mut params, + state + .data + .fetch_component_as::("cb_opaque_background")?, + |settings| &mut settings.general.opaque_background, + Some(|frontend, _| { + frontend.tasks.push(FrontendTask::RefreshBackground); + }), + )?; + + init_setting_checkbox( + &mut params, + state + .data + .fetch_component_as::("cb_xwayland_by_default")?, + |settings| &mut settings.tweaks.xwayland_by_default, + None, + )?; + + Ok(Self { state }) + } +} diff --git a/dash-frontend/src/task.rs b/dash-frontend/src/task.rs new file mode 100644 index 0000000..f6af456 --- /dev/null +++ b/dash-frontend/src/task.rs @@ -0,0 +1,27 @@ +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; + +#[derive(Clone)] +pub struct Tasks(Rc>>) +where + TaskType: Clone; + +impl Tasks { + pub fn new() -> Self { + Self(Rc::new(RefCell::new(VecDeque::new()))) + } + + pub fn push(&self, task: TaskType) { + self.0.borrow_mut().push_back(task); + } + + pub fn drain(&mut self) -> VecDeque { + let mut tasks = self.0.borrow_mut(); + std::mem::take(&mut *tasks) + } +} + +impl Default for Tasks { + fn default() -> Self { + Self::new() + } +} diff --git a/dash-frontend/src/util/desktop_finder.rs b/dash-frontend/src/util/desktop_finder.rs new file mode 100644 index 0000000..8f0b6b5 --- /dev/null +++ b/dash-frontend/src/util/desktop_finder.rs @@ -0,0 +1,131 @@ +use gio::prelude::{AppInfoExt, IconExt}; +use gtk::traits::IconThemeExt; + +#[derive(Debug, Clone)] +#[allow(dead_code)] // TODO: remove this +pub struct DesktopEntry { + pub exec_path: String, + pub exec_args: Vec, + pub app_name: String, + pub icon_path: Option, + pub categories: Vec, +} + +#[allow(dead_code)] // TODO: remove this +pub struct EntrySearchCell { + pub exec_path: String, + pub exec_args: Vec, + pub app_name: String, + pub icon_name: Option, + pub categories: Vec, +} + +const CMD_BLACKLIST: [&str; 1] = [ + "lsp-plugins", // LSP Plugins collection. They clutter the application list a lot +]; + +const CATEGORY_TYPE_BLACKLIST: [&str; 5] = ["GTK", "Qt", "X-XFCE", "X-Bluetooth", "ConsoleOnly"]; + +pub fn find_entries() -> anyhow::Result> { + let Some(icon_theme) = gtk::IconTheme::default() else { + anyhow::bail!("Failed to get current icon theme information"); + }; + + let mut res = Vec::::new(); + + let info = gio::AppInfo::all(); + + log::debug!("app entry count {}", info.len()); + + 'outer: for app_entry in info { + let Some(app_entry_id) = app_entry.id() else { + log::warn!( + "failed to get desktop entry ID for application named \"{}\"", + app_entry.name() + ); + continue; + }; + + let Some(desktop_app) = gio::DesktopAppInfo::new(&app_entry_id) else { + log::warn!( + "failed to find desktop app file from application named \"{}\"", + app_entry.name() + ); + continue; + }; + + if desktop_app.is_nodisplay() || desktop_app.is_hidden() { + continue; + } + + let Some(cmd) = desktop_app.commandline() else { + continue; + }; + + let name = String::from(desktop_app.name()); + + let exec = String::from(cmd.to_string_lossy()); + + for blacklisted in CMD_BLACKLIST { + if exec.contains(blacklisted) { + continue 'outer; + } + } + + let (exec_path, exec_args) = match exec.split_once(" ") { + Some((left, right)) => ( + String::from(left), + right + .split(" ") + .filter(|arg| !arg.starts_with('%')) // exclude arguments like "%f" + .map(String::from) + .collect(), + ), + None => (exec, Vec::new()), + }; + + let icon_path = match desktop_app.icon() { + Some(icon) => { + if let Some(icon_str) = icon.to_string() { + if let Some(s_icon) = icon_theme.lookup_icon(&icon_str, 128, gtk::IconLookupFlags::GENERIC_FALLBACK) { + s_icon.filename().map(|p| String::from(p.to_string_lossy())) + } else { + None + } + } else { + None + } + } + None => None, + }; + + let categories: Vec = match desktop_app.categories() { + Some(categories) => categories + .split(";") + .filter(|s| !s.is_empty()) + .filter(|s| { + for b in CATEGORY_TYPE_BLACKLIST { + if *s == b { + return false; + } + } + true + }) + .map(String::from) + .collect(), + None => Vec::new(), + }; + + let entry = DesktopEntry { + app_name: name, + categories, + exec_path, + exec_args, + icon_path, + }; + + res.push(entry); + } + + Ok(res) +} diff --git a/dash-frontend/src/util/mod.rs b/dash-frontend/src/util/mod.rs new file mode 100644 index 0000000..1331ba1 --- /dev/null +++ b/dash-frontend/src/util/mod.rs @@ -0,0 +1,4 @@ +pub mod desktop_finder; +pub mod pactl_wrapper; +pub mod popup_manager; +pub mod toast_manager; diff --git a/dash-frontend/src/util/pactl_wrapper.rs b/dash-frontend/src/util/pactl_wrapper.rs new file mode 100644 index 0000000..6fe9f2c --- /dev/null +++ b/dash-frontend/src/util/pactl_wrapper.rs @@ -0,0 +1,312 @@ +use std::collections::{BTreeMap, HashMap}; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct VolumeChannel { + pub value: u32, // 48231 + pub value_percent: String, // "80%" + pub db: String, // "-5.81 dB" +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Volume { + // WiVRn and other devices + pub aux0: Option, + pub aux1: Option, + + // Analog and HDMI devices + #[serde(rename = "front-left")] + pub front_left: Option, + + #[serde(rename = "front-right")] + pub front_right: Option, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Sink { + pub index: u32, // 123 + pub state: String, // "RUNNING" / "SUSPENDED" + pub name: String, // alsa_output.pci-0000_0c_00.4.analog-stereo + pub description: String, // Starship/Matisse HD Audio Controller Analog Stereo + pub mute: bool, // false + pub volume: Volume, + pub properties: HashMap, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct SourceProperties { + #[serde(rename = "device.name")] + pub device_name: Option, // "alsa_card.pci-0000_0b_00.1" + #[serde(rename = "device.class")] + pub device_class: Option, // "monitor", "sound" + #[serde(rename = "alsa.card_name")] + pub card_name: Option, // "Valve VR Radio & HMD Mic" + #[serde(rename = "alsa.components")] + pub components: Option, // USB28de:2102 + #[serde(rename = "device.vendor_name")] + pub vendor_name: Option, // Valve Software +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Source { + pub index: u32, // 123 + pub state: String, // "RUNNING" / "SUSPENDED" + pub name: String, // alsa_input.pci-0000_0c_00.4.analog-stereo + pub description: String, // Valve VR Radio & HMD Mic Mono + pub mute: bool, // false + pub volume: Volume, + pub properties: SourceProperties, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct CardProperties { + #[serde(rename = "device.description")] + pub device_description: String, // Starship/Matisse HD Audio Controller + + #[serde(rename = "device.name")] + pub device_name: String, // alsa_card.pci-0000_0c_00.4 + + #[serde(rename = "device.nick")] + pub device_nick: String, // HD-Audio Generic +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct CardProfile { + pub description: String, // "Digital Stereo (HDMI 2) Output", "Analog Stereo Output", + pub sinks: u32, // 1 + pub sources: u32, // 0 + pub priority: u32, // 6500 + pub available: bool, // true +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct CardPort { + pub description: String, // "HDMI / DisplayPort 2" + pub r#type: String, // "HDMI" + pub profiles: Vec, // "output:hdmi-stereo-extra1", "output:hdmi-surround-extra1", "output:analog-stereo", "output:analog-stereo+input:analog-stereo" + + // example: + // "port.type": "hdmi" + // "device.product_name": "Index HMD" + pub properties: HashMap, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Card { + pub index: u32, // 57 + pub name: String, // alsa_card.pci-0000_0c_00.4 + pub active_profile: String, // output:analog-stereo + pub properties: CardProperties, + pub profiles: BTreeMap, // key: "output:analog-stereo" + pub ports: BTreeMap, // key: "analog-output-lineout" +} + +// ######################################## +// ~ sinks ~ +// ######################################## + +pub fn list_sinks() -> anyhow::Result> { + let output = std::process::Command::new("pactl") + .arg("--format=json") + .arg("list") + .arg("sinks") + .output()?; + + if !output.status.success() { + anyhow::bail!("pactl exit status {}", output.status); + } + + let json_str = std::str::from_utf8(&output.stdout)?; + let sinks: Vec = serde_json::from_str(json_str)?; + Ok(sinks) +} + +pub fn get_default_sink(sinks: &[Sink]) -> anyhow::Result> { + let output = std::process::Command::new("pactl").arg("get-default-sink").output()?; + + let utf8_name = std::str::from_utf8(&output.stdout)?.trim(); + + for sink in sinks { + if sink.name == utf8_name { + return Ok(Some(sink.clone())); + } + } + + Ok(None) +} + +pub fn set_default_sink(sink_index: u32) -> anyhow::Result<()> { + std::process::Command::new("pactl") + .arg("set-default-sink") + .arg(format!("{}", sink_index)) + .output()?; + + Ok(()) +} + +pub fn get_sink_volume(sink: &Sink) -> anyhow::Result { + let volume_channel = { + if let Some(front_left) = &sink.volume.front_left { + front_left + } else if let Some(aux0) = &sink.volume.aux0 { + aux0 + } else { + return Ok(0.0); // fail silently + } + }; + + let Some(pair) = volume_channel.value_percent.split_once("%") else { + anyhow::bail!("volume percentage invalid"); // shouldn't happen + }; + + let percent_num: f32 = pair.0.parse().unwrap_or(0.0); + Ok(percent_num / 100.0) +} + +pub fn set_sink_volume(sink_index: u32, volume: f32) -> anyhow::Result<()> { + let target_vol = (volume * 100.0).clamp(0.0, 150.0); // limit to 150% + + std::process::Command::new("pactl") + .arg("set-sink-volume") + .arg(format!("{}", sink_index)) + .arg(format!("{}%", target_vol)) + .output()?; + + Ok(()) +} + +pub fn set_sink_mute(sink_index: u32, mute: bool) -> anyhow::Result<()> { + std::process::Command::new("pactl") + .arg("set-sink-mute") + .arg(format!("{}", sink_index)) + .arg(format!("{}", mute as i32)) + .output()?; + + Ok(()) +} + +// ######################################## +// ~ sources ~ +// ######################################## + +pub fn list_sources() -> anyhow::Result> { + let output = std::process::Command::new("pactl") + .arg("--format=json") + .arg("list") + .arg("sources") + .output()?; + + if !output.status.success() { + anyhow::bail!("pactl exit status {}", output.status); + } + + let json_str = std::str::from_utf8(&output.stdout)?; + let mut sources: Vec = serde_json::from_str(json_str)?; + + // exclude all monitor sources + sources.retain(|source| match &source.properties.device_class { + Some(c) => c != "monitor", + None => false, + }); + + Ok(sources) +} + +pub fn get_default_source(sources: &[Source]) -> anyhow::Result> { + let output = std::process::Command::new("pactl").arg("get-default-source").output()?; + + let utf8_name = std::str::from_utf8(&output.stdout)?.trim(); + + for source in sources { + if source.name == utf8_name { + return Ok(Some(source.clone())); + } + } + + Ok(None) +} + +pub fn set_default_source(source_index: u32) -> anyhow::Result<()> { + std::process::Command::new("pactl") + .arg("set-default-source") + .arg(format!("{}", source_index)) + .output()?; + + Ok(()) +} + +pub fn get_source_volume(source: &Source) -> anyhow::Result { + let volume_channel = { + if let Some(front_left) = &source.volume.front_left { + front_left + } else if let Some(aux0) = &source.volume.aux0 { + aux0 + } else { + return Ok(0.0); // fail silently + } + }; + + let Some(pair) = volume_channel.value_percent.split_once("%") else { + anyhow::bail!("volume percentage invalid"); // shouldn't happen + }; + + let percent_num: f32 = pair.0.parse().unwrap_or(0.0); + Ok(percent_num / 100.0) +} + +pub fn set_source_volume(source_index: u32, volume: f32) -> anyhow::Result<()> { + let target_vol = (volume * 100.0).clamp(0.0, 150.0); // limit to 150% + + std::process::Command::new("pactl") + .arg("set-source-volume") + .arg(format!("{}", source_index)) + .arg(format!("{}%", target_vol)) + .output()?; + + Ok(()) +} + +pub fn set_source_mute(source_index: u32, mute: bool) -> anyhow::Result<()> { + std::process::Command::new("pactl") + .arg("set-source-mute") + .arg(format!("{}", source_index)) + .arg(format!("{}", mute as i32)) + .output()?; + + Ok(()) +} + +// ######################################## +// ~ cards ~ +// ######################################## + +pub fn list_cards() -> anyhow::Result> { + let output = std::process::Command::new("pactl") + .arg("--format=json") + .arg("list") + .arg("cards") + .output()?; + + if !output.status.success() { + anyhow::bail!("pactl exit status {}", output.status); + } + + let json_str = std::str::from_utf8(&output.stdout)?; + let mut cards: Vec = serde_json::from_str(json_str)?; + + // exclude card which has "Loopback" in name + cards.retain(|card| card.properties.device_nick != "Loopback"); + + Ok(cards) +} + +pub fn set_card_profile(card_index: u32, profile: &str) -> anyhow::Result<()> { + std::process::Command::new("pactl") + .arg("set-card-profile") + .arg(format!("{}", card_index)) + .arg(profile) + .output()?; + + Ok(()) +} diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs new file mode 100644 index 0000000..5c4e665 --- /dev/null +++ b/dash-frontend/src/util/popup_manager.rs @@ -0,0 +1,182 @@ +use std::{ + cell::RefCell, + rc::{Rc, Weak}, +}; + +use wgui::{ + assets::AssetPath, + components::button::ComponentButton, + event::{EventAlterables, StyleSetRequest}, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, LayoutTask, LayoutTasks, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + taffy::Display, + widget::label::WidgetLabel, +}; + +use crate::frontend::{FrontendTask, FrontendTasks}; + +pub struct PopupManagerParams { + pub parent_id: WidgetID, +} + +struct State { + popup_stack: Vec>>, +} + +pub struct MountedPopup { + #[allow(dead_code)] + state: ParserState, + id_root: WidgetID, // decorations of a popup + layout_tasks: LayoutTasks, + frontend_tasks: FrontendTasks, +} + +struct MountedPopupState { + mounted_popup: Option, +} + +#[derive(Clone)] +pub struct PopupHandle { + state: Rc>, +} + +impl PopupHandle { + pub fn close(&self) { + self.state.borrow_mut().mounted_popup = None; // Drop will be called + } +} + +pub struct PopupManager { + state: Rc>, + parent_id: WidgetID, +} + +pub struct PopupContentFuncData<'a> { + pub layout: &'a mut Layout, + pub handle: PopupHandle, + pub id_content: WidgetID, +} + +#[derive(Clone)] +pub struct MountPopupParams { + pub title: Translation, + pub on_content: Rc anyhow::Result<()>>, +} + +impl Drop for MountedPopup { + fn drop(&mut self) { + self.layout_tasks.push(LayoutTask::RemoveWidget(self.id_root)); + self.frontend_tasks.push(FrontendTask::RefreshPopupManager); + } +} + +impl State { + fn refresh_stack(&mut self, alterables: &mut EventAlterables) { + // show only the topmost popup + self.popup_stack.retain(|weak| { + let Some(popup) = weak.upgrade() else { + return false; + }; + popup.borrow_mut().mounted_popup.is_some() + }); + + for (idx, popup) in self.popup_stack.iter().enumerate() { + let popup = popup.upgrade().unwrap(); // safe + let popup = popup.borrow_mut(); + let mounted_popup = popup.mounted_popup.as_ref().unwrap(); // safe; + + alterables.set_style( + mounted_popup.id_root, + StyleSetRequest::Display(if idx == self.popup_stack.len() - 1 { + Display::Flex + } else { + Display::None + }), + ); + } + } +} + +impl PopupManager { + pub fn new(params: PopupManagerParams) -> Self { + Self { + parent_id: params.parent_id, + state: Rc::new(RefCell::new(State { + popup_stack: Vec::new(), + })), + } + } + + pub fn refresh(&self, alterables: &mut EventAlterables) { + let mut state = self.state.borrow_mut(); + state.refresh_stack(alterables); + } + + /// Mount a new popup on top of the existing popup stack. + /// Only the topmost popup is visible. + pub fn mount_popup( + &mut self, + globals: WguiGlobals, + layout: &mut Layout, + frontend_tasks: FrontendTasks, + params: MountPopupParams, + ) -> anyhow::Result<()> { + let doc_params = &ParseDocumentParams { + globals: globals.clone(), + path: AssetPath::BuiltIn("gui/view/popup_window.xml"), + extra: Default::default(), + }; + let state = wgui::parser::parse_from_assets(doc_params, layout, self.parent_id)?; + + let id_root = state.get_widget_id("root")?; + let id_content = state.get_widget_id("content")?; + + { + let mut label_title = state.fetch_widget_as::(&layout.state, "popup_title")?; + label_title.set_text_simple(&mut globals.get(), params.title); + } + + let but_back = state.fetch_component_as::("but_back")?; + + let mounted_popup = MountedPopup { + state, + id_root, + layout_tasks: layout.tasks.clone(), + frontend_tasks: frontend_tasks.clone(), + }; + + let mounted_popup_state = MountedPopupState { + mounted_popup: Some(mounted_popup), + }; + + let popup_handle = PopupHandle { + state: Rc::new(RefCell::new(mounted_popup_state)), + }; + + let mut state = self.state.borrow_mut(); + state.popup_stack.push(Rc::downgrade(&popup_handle.state)); + + but_back.on_click({ + let popup_handle = Rc::downgrade(&popup_handle.state); + Box::new(move |_common, _evt| { + if let Some(popup_handle) = popup_handle.upgrade() { + popup_handle.borrow_mut().mounted_popup = None; // will call Drop + } + Ok(()) + }) + }); + + frontend_tasks.push(FrontendTask::RefreshPopupManager); + + // mount user-set popup content + (*params.on_content)(PopupContentFuncData { + layout, + handle: popup_handle.clone(), + id_content, + })?; + + Ok(()) + } +} diff --git a/dash-frontend/src/util/toast_manager.rs b/dash-frontend/src/util/toast_manager.rs new file mode 100644 index 0000000..d6eb3bd --- /dev/null +++ b/dash-frontend/src/util/toast_manager.rs @@ -0,0 +1,190 @@ +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; + +use glam::{Mat4, Vec3}; +use wgui::{ + animation::{Animation, AnimationEasing}, + components::tooltip::{TOOLTIP_BORDER_COLOR, TOOLTIP_COLOR}, + drawing::Color, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, LayoutTask, LayoutTasks, WidgetID}, + renderer_vk::{ + text::{FontWeight, TextStyle}, + util::centered_matrix, + }, + taffy::{ + self, + prelude::{length, percent}, + }, + widget::{ + div::WidgetDiv, + label::{WidgetLabel, WidgetLabelParams}, + rectangle::{WidgetRectangle, WidgetRectangleParams}, + util::WLength, + }, +}; + +struct MountedToast { + #[allow(dead_code)] + id_root: WidgetID, // decorations of a toast + layout_tasks: LayoutTasks, +} + +struct State { + toast: Option, + queue: VecDeque, + timeout: u32, // in ticks +} + +pub struct ToastManager { + state: Rc>, + needs_tick: bool, +} + +impl Drop for MountedToast { + fn drop(&mut self) { + self.layout_tasks.push(LayoutTask::RemoveWidget(self.id_root)); + } +} + +const TOAST_DURATION_TICKS: u32 = 90; + +impl ToastManager { + pub fn new() -> Self { + Self { + state: Rc::new(RefCell::new(State { + toast: None, + timeout: 0, + queue: VecDeque::new(), + })), + needs_tick: false, + } + } + + fn mount_toast( + &self, + globals: &WguiGlobals, + layout: &mut Layout, + state: &mut State, + content: Translation, + ) -> anyhow::Result<()> { + let mut globals = globals.get(); + + let (root, _) = layout.add_topmost_child( + WidgetDiv::create(), + taffy::Style { + position: taffy::Position::Absolute, + size: taffy::Size { + width: percent(1.0), + height: percent(0.8), + }, + align_items: Some(taffy::AlignItems::End), + justify_content: Some(taffy::JustifyContent::Center), + ..Default::default() + }, + )?; + + let (rect, _) = layout.add_child( + root.id, + WidgetRectangle::create(WidgetRectangleParams { + color: TOOLTIP_COLOR, + border_color: TOOLTIP_BORDER_COLOR, + border: 2.0, + round: WLength::Percent(1.0), + ..Default::default() + }), + taffy::Style { + position: taffy::Position::Relative, + gap: length(4.0), + padding: taffy::Rect { + left: length(16.0), + right: length(16.0), + top: length(8.0), + bottom: length(8.0), + }, + ..Default::default() + }, + )?; + + let (label, _) = layout.add_child( + rect.id, + WidgetLabel::create( + &mut globals, + WidgetLabelParams { + content, + style: TextStyle { + weight: Some(FontWeight::Bold), + ..Default::default() + }, + }, + ), + taffy::Style { ..Default::default() }, + )?; + + // show-up animation + layout.animations.add(Animation::new( + rect.id, + 160, + AnimationEasing::Linear, + Box::new(move |common, data| { + let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0)); + let opacity = 1.0 - AnimationEasing::OutQuint.interpolate(((data.pos - 0.75) * 4.0).clamp(0.0, 1.0)); + let scale = AnimationEasing::OutBack.interpolate((data.pos * 4.0).min(1.0)); + + { + let mtx = Mat4::from_translation(Vec3::new(0.0, (1.0 - pos_showup) * 100.0, 0.0)) + * Mat4::from_scale(Vec3::new(scale, scale, 1.0)); + data.data.transform = centered_matrix(data.widget_boundary.size, &mtx); + } + + let rect = data.obj.get_as_mut::().unwrap(); + rect.params.color.a = opacity; + rect.params.border_color.a = opacity; + + let mut label = common.state.widgets.get_as::(label.id).unwrap(); + label.set_color(common, Color::new(1.0, 1.0, 1.0, opacity), true); + common.alterables.mark_redraw(); + }), + )); + + state.toast = Some(MountedToast { + id_root: root.id, + layout_tasks: layout.tasks.clone(), + }); + + Ok(()) + } + + pub fn tick(&mut self, globals: &WguiGlobals, layout: &mut Layout) -> anyhow::Result<()> { + if !self.needs_tick { + return Ok(()); + } + + let mut state = self.state.borrow_mut(); + + if state.timeout > 0 { + state.timeout -= 1; + } + + if state.timeout == 0 { + state.toast = None; + state.timeout = TOAST_DURATION_TICKS; + // mount next + if let Some(content) = state.queue.pop_front() { + self.mount_toast(globals, layout, &mut state, content)?; + } + } + + if state.queue.is_empty() && state.toast.is_none() { + self.needs_tick = false; + } + + Ok(()) + } + + pub fn push(&mut self, content: Translation) { + let mut state = self.state.borrow_mut(); + state.queue.push_back(content); + self.needs_tick = true; + } +} diff --git a/dash-frontend/src/various.rs b/dash-frontend/src/various.rs new file mode 100644 index 0000000..7f48cee --- /dev/null +++ b/dash-frontend/src/various.rs @@ -0,0 +1,6 @@ +pub fn get_username() -> String { + match std::env::var("USER") { + Ok(user) => user, + Err(_) => String::from("anonymous"), + } +} diff --git a/dash-frontend/src/views/app_launcher.rs b/dash-frontend/src/views/app_launcher.rs new file mode 100644 index 0000000..f8a2ed9 --- /dev/null +++ b/dash-frontend/src/views/app_launcher.rs @@ -0,0 +1,63 @@ +use std::{collections::HashMap, rc::Rc}; + +use wgui::{ + assets::AssetPath, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + widget::label::WidgetLabel, +}; + +use crate::util::desktop_finder::DesktopEntry; + +pub struct View { + #[allow(dead_code)] + pub state: ParserState, + //entry: DesktopEntry, +} + +pub struct Params<'a> { + pub globals: WguiGlobals, + pub entry: DesktopEntry, + pub layout: &'a mut Layout, + pub parent_id: WidgetID, +} + +impl View { + pub fn new(params: Params) -> anyhow::Result { + let doc_params = &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/view/app_launcher.xml"), + extra: Default::default(), + }; + + let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?; + let id_icon_parent = state.get_widget_id("icon_parent")?; + + // app icon + if let Some(icon_path) = ¶ms.entry.icon_path { + let mut template_params: HashMap, Rc> = HashMap::new(); + template_params.insert("path".into(), icon_path.as_str().into()); + state.instantiate_template( + doc_params, + "ApplicationIcon", + params.layout, + id_icon_parent, + template_params, + )?; + } + + let mut label_title = state.fetch_widget_as::(¶ms.layout.state, "label_title")?; + + label_title.set_text_simple( + &mut params.globals.get(), + Translation::from_raw_text(¶ms.entry.app_name), + ); + + Ok(Self { + //entry: params.entry, + state, + }) + } +} diff --git a/dash-frontend/src/views/audio_settings.rs b/dash-frontend/src/views/audio_settings.rs new file mode 100644 index 0000000..b24b29b --- /dev/null +++ b/dash-frontend/src/views/audio_settings.rs @@ -0,0 +1,967 @@ +use std::{collections::HashMap, rc::Rc}; + +use wgui::{ + assets::AssetPath, + components::{ + self, + button::{ButtonClickCallback, ComponentButton}, + checkbox::ComponentCheckbox, + slider::ComponentSlider, + }, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + widget::ConstructEssentials, +}; + +use crate::{ + frontend::{FrontendTask, FrontendTasks}, + task::Tasks, + util::pactl_wrapper, +}; + +#[derive(Clone)] +#[allow(clippy::large_enum_variant)] +enum CurrentMode { + Sinks, + Sources, + Cards, + CardProfileSelector(pactl_wrapper::Card), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SearchType { + Sink, + #[allow(dead_code)] + Source, // might be useful in the future +} + +#[derive(Clone)] +struct IndexAndVolume { + idx: u32, + volume: f32, +} + +#[derive(Clone)] +struct CardAndProfileName { + card: pactl_wrapper::Card, + profile_name: String, +} + +#[derive(Clone)] +#[allow(clippy::large_enum_variant)] +enum ViewTask { + Remount, + SetMode(CurrentMode), + AutoSwitch, + SetSinkVolume(IndexAndVolume), + SetSourceVolume(IndexAndVolume), + SetCardProfile(CardAndProfileName), +} + +type ViewTasks = Tasks; + +pub struct View { + tasks: ViewTasks, + frontend_tasks: FrontendTasks, // used only for toasts + on_update: Rc, + + globals: WguiGlobals, + + #[allow(dead_code)] + state: ParserState, + + //entry: DesktopEntry, + mode: CurrentMode, + + id_devices: WidgetID, + + btn_sinks: Rc, + btn_sources: Rc, + btn_cards: Rc, +} + +pub struct Params<'a> { + pub globals: WguiGlobals, + pub frontend_tasks: FrontendTasks, + pub layout: &'a mut Layout, + pub parent_id: WidgetID, + pub on_update: Rc, +} + +struct ProfileDisplayName { + name: String, + icon_path: &'static str, + is_vr: bool, +} + +struct SelectorCell { + key: String, + display_text: String, + icon_path: &'static str, +} + +struct MultiSelectorParams<'a> { + cells: &'a [SelectorCell], + def_cell: &'a str, + ess: &'a mut ConstructEssentials<'a>, + on_click: Rc, +} + +fn mount_multi_selector(params: MultiSelectorParams) -> anyhow::Result<()> { + let globals = params.ess.layout.state.globals.clone(); + let accent_color = globals.get().defaults.accent_color; + + for cell in params.cells { + let highlighted = cell.key == params.def_cell; + let color = if highlighted { Some(accent_color) } else { None }; + + // button + let (_, button) = components::button::construct( + params.ess, + components::button::Params { + text: Some(Translation::from_raw_text(&cell.display_text)), + sprite_src: Some(AssetPath::BuiltIn(cell.icon_path)), + color, + ..Default::default() + }, + )?; + + button.on_click({ + let on_click = params.on_click.clone(); + let key = cell.key.clone(); + Box::new(move |_, _| { + (*on_click)(key.as_str()); + Ok(()) + }) + }); + } + + Ok(()) +} + +fn get_card_from_sink<'a>( + sink: &pactl_wrapper::Sink, + cards: &'a [pactl_wrapper::Card], +) -> Option<&'a pactl_wrapper::Card> { + let Some(sink_dev_name) = &sink.properties.get("device.name") else { + return None; + }; + + cards.iter().find(|&card| **sink_dev_name == card.name).map(|v| v as _) +} + +fn get_card_from_source<'a>( + source: &pactl_wrapper::Source, + cards: &'a [pactl_wrapper::Card], +) -> Option<&'a pactl_wrapper::Card> { + let Some(source_dev_name) = &source.properties.device_name else { + return None; + }; + + cards + .iter() + .find(|&card| **source_dev_name == card.name) + .map(|v| v as _) +} + +fn does_string_mention_hmd_sink(input: &str) -> bool { + let lwr = input.to_lowercase(); + lwr.contains("hmd") || // generic hmd name detected + lwr.contains("index") || // Valve hardware + lwr.contains("oculus") || // Oculus + lwr.contains("rift") || // Also Oculus + lwr.contains("beyond") // Bigscreen Beyond +} + +fn does_string_mention_hmd_source(input: &str) -> bool { + let lwr = input.to_lowercase(); + lwr.contains("hmd") || // generic hmd name detected + lwr.contains("valve") || // Valve hardware + lwr.contains("oculus") || // Oculus + lwr.contains("beyond") // Bigscreen Beyond +} + +fn is_card_mentioning_hmd(card: &pactl_wrapper::Card) -> bool { + does_string_mention_hmd_sink(&card.properties.device_name) +} + +fn is_source_mentioning_hmd(source: &pactl_wrapper::Source) -> bool { + if let Some(source_card_name) = &source.properties.card_name + && does_string_mention_hmd_source(source_card_name) + { + return true; + } + + // WiVRn + if source.name == "wivrn.source" { + return true; + } + + false +} + +fn get_profile_display_name(profile_name: &str, card: &pactl_wrapper::Card) -> ProfileDisplayName { + let Some(profile) = card.profiles.get(profile_name) else { + // fallback + return ProfileDisplayName { + name: profile_name.into(), + icon_path: "dashboard/binary.svg", + is_vr: false, + }; + }; + + let mut out_icon_path: &'static str; + let mut is_vr = false; + + let prof = profile_name.to_lowercase(); + if prof.contains("analog") { + out_icon_path = "dashboard/minijack.svg"; + } else if prof.contains("iec" /* digital */) { + out_icon_path = "dashboard/binary.svg"; + } else if prof.contains("hdmi") { + out_icon_path = "dashboard/displayport.svg"; + } else if prof.contains("off") { + out_icon_path = "dashboard/sleep.svg"; + } else if prof.contains("input") { + out_icon_path = "dashboard/microphone.svg"; + } else { + out_icon_path = "dashboard/volume.svg"; // Default fallback + } + + // All ports are tied to this VR headset, assign all of them to the VR icon + if is_card_mentioning_hmd(card) { + if prof.contains("mic") { + // Probably microphone + out_icon_path = "dashboard/microphone.svg"; + } else { + out_icon_path = "dashboard/vr.svg"; + } + } + + let mut out_name: Option = None; + + for port in card.ports.values() { + // Find profile + for port_profile in &port.profiles { + if !port_profile.contains("stereo") { + continue; // we only want stereo, not surround or other types + } + + if port_profile != profile_name { + continue; + } + + // Exact match! Use its device name + let Some(product_name) = port.properties.get("device.product.name") else { + continue; + }; + + out_name = Some(product_name.clone()); + + if does_string_mention_hmd_sink(product_name) { + // VR icon + out_icon_path = "dashboard/vr.svg"; + is_vr = true; + } else { + // Monitor icon + out_icon_path = "dashboard/displayport.svg"; + } + + break; + } + } + + ProfileDisplayName { + name: if let Some(name) = out_name { + name + } else { + profile.description.clone() + }, + icon_path: out_icon_path, + is_vr, + } +} + +fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> { + ParseDocumentParams { + globals: globals.clone(), + path: AssetPath::BuiltIn("gui/view/audio_settings.xml"), + extra: Default::default(), + } +} + +trait DeviceControl { + fn on_volume_request(&self) -> anyhow::Result; + fn on_check(&self) -> anyhow::Result<()>; + fn on_mute_toggle(&self) -> anyhow::Result<()>; + fn on_volume_change(&self, volume: f32) -> anyhow::Result<()>; +} + +struct ControlSink { + tasks: ViewTasks, + on_update: Rc, + sink: pactl_wrapper::Sink, +} + +impl ControlSink { + fn new(tasks: ViewTasks, on_update: Rc, sink: pactl_wrapper::Sink) -> Self { + Self { tasks, sink, on_update } + } +} + +impl DeviceControl for ControlSink { + fn on_volume_request(&self) -> anyhow::Result { + let volume = pactl_wrapper::get_sink_volume(&self.sink)?; + Ok(volume) + } + + fn on_check(&self) -> anyhow::Result<()> { + pactl_wrapper::set_default_sink(self.sink.index)?; + self.tasks.push(ViewTask::Remount); + (*self.on_update)(); + Ok(()) + } + + fn on_mute_toggle(&self) -> anyhow::Result<()> { + pactl_wrapper::set_sink_mute(self.sink.index, !self.sink.mute)?; + self.tasks.push(ViewTask::Remount); + (*self.on_update)(); + Ok(()) + } + + fn on_volume_change(&self, volume: f32) -> anyhow::Result<()> { + self.tasks.push(ViewTask::SetSinkVolume(IndexAndVolume { + idx: self.sink.index, + volume, + })); + (*self.on_update)(); + Ok(()) + } +} + +struct ControlSource { + tasks: ViewTasks, + on_update: Rc, + source: pactl_wrapper::Source, +} + +impl ControlSource { + fn new(tasks: ViewTasks, on_update: Rc, source: pactl_wrapper::Source) -> Self { + Self { + tasks, + source, + on_update, + } + } +} + +impl DeviceControl for ControlSource { + fn on_volume_request(&self) -> anyhow::Result { + let volume = pactl_wrapper::get_source_volume(&self.source)?; + Ok(volume) + } + + fn on_check(&self) -> anyhow::Result<()> { + pactl_wrapper::set_default_source(self.source.index)?; + self.tasks.push(ViewTask::Remount); + (*self.on_update)(); + Ok(()) + } + + fn on_mute_toggle(&self) -> anyhow::Result<()> { + pactl_wrapper::set_source_mute(self.source.index, !self.source.mute)?; + self.tasks.push(ViewTask::Remount); + (*self.on_update)(); + Ok(()) + } + + fn on_volume_change(&self, volume: f32) -> anyhow::Result<()> { + self.tasks.push(ViewTask::SetSourceVolume(IndexAndVolume { + idx: self.source.index, + volume, + })); + (*self.on_update)(); + Ok(()) + } +} + +struct MountCardParams<'a> { + layout: &'a mut Layout, + card: &'a pactl_wrapper::Card, +} + +struct MountDeviceSliderParams<'a> { + layout: &'a mut Layout, + control: Rc, + checked: bool, + muted: bool, + disp: Option, + alt_desc: String, +} + +fn switch_sink_card( + frontend_tasks: &FrontendTasks, + card: &pactl_wrapper::Card, + profile_name: &str, + name: &ProfileDisplayName, +) -> anyhow::Result<()> { + let card_index = card.index; + let profile = profile_name.to_string(); + pactl_wrapper::set_card_profile(card_index, &profile)?; + let sinks = pactl_wrapper::list_sinks()?; + + let mut sink_found = false; + for sink in &sinks { + if let Some(device_name) = sink.properties.get("device.name") + && device_name == &card.name + { + pactl_wrapper::set_default_sink(sink.index)?; + sink_found = true; + break; + } + } + + if sink_found { + frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key( + format!("[AUDIO.SPEAKERS_SET_SUCCESSFULLY]: {}", name.name).as_str(), + ))); + } else { + frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key( + format!("[AUDIO.DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED]: {}", name.name).as_str(), + ))); + } + + Ok(()) +} + +fn switch_source(frontend_tasks: &FrontendTasks, source: &pactl_wrapper::Source) -> anyhow::Result<()> { + match pactl_wrapper::set_default_source(source.index) { + Ok(()) => { + frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key( + format!( + "[AUDIO.MICROPHONE_SET_SUCCESSFULLY]: {}", + if let Some(card_name) = &source.properties.card_name { + card_name + } else { + &source.description + } + ) + .as_str(), + ))); + Ok(()) + } + Err(e) => { + Translation::from_translation_key(format!("[AUDIO.FAILED_TO_SWITCH_MICROPHONE]: {:?}", e).as_str()); + Err(e) + } + } +} + +fn switch_to_vr_microphone(frontend_tasks: &FrontendTasks) -> anyhow::Result<()> { + let sources = pactl_wrapper::list_sources()?; + let mut switched = false; + + for source in &sources { + if is_source_mentioning_hmd(source) { + switch_source(frontend_tasks, source)?; + switched = true; + break; + } + } + + if !switched { + frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key( + "AUDIO.NO_VR_MICROPHONE_SWITCH_MANUALLY", + ))); + } + + Ok(()) +} + +#[derive(Debug, Clone)] +struct CardPriorityResult<'a> { + priority: u32, + name: String, + card: &'a pactl_wrapper::Card, +} + +fn get_card_best_profile<'a>(card: &'a pactl_wrapper::Card, search_type: SearchType) -> Option> { + let mut best_priority = 0; + let mut best_profile_name = ""; + let mut best_profile: Option<&'a pactl_wrapper::CardProfile> = None; + + for (profile_name, profile) in &card.profiles { + match search_type { + SearchType::Sink if profile.sinks == 0 => continue, + SearchType::Source if profile.sources == 0 => continue, + _ => {} + } + + if profile.priority > best_priority { + best_priority = profile.priority; + best_profile = Some(profile); + best_profile_name = profile_name; + } + } + + best_profile?; // do not proceed if no profile was found + + Some(CardPriorityResult { + priority: best_priority, + name: best_profile_name.to_string(), + card, + }) +} + +fn get_best_profile_from_array<'a>(arr: &[CardPriorityResult<'a>]) -> Option> { + let mut best_priority = 0; + let mut res: Option> = None; + + for cell in arr { + if cell.priority > best_priority { + best_priority = cell.priority; + res = Some(cell.clone()); + } + } + + res +} + +fn switch_to_vr_speakers(frontend_tasks: &FrontendTasks) -> anyhow::Result<()> { + let cards = pactl_wrapper::list_cards()?; + let mut best_profiles = Vec::new(); + + for card in &cards { + if !is_card_mentioning_hmd(card) { + continue; + } + if let Some(best_profile) = get_card_best_profile(card, SearchType::Sink) { + best_profiles.push(best_profile); + } + } + + if !best_profiles.is_empty() { + let best_profile = get_best_profile_from_array(&best_profiles).unwrap(); + let name = get_profile_display_name(&best_profile.name, best_profile.card); + switch_sink_card(frontend_tasks, best_profile.card, &best_profile.name, &name)?; + return Ok(()); + } + + // There aren't any cards which mention VR explicitly. Time for plan B. + for card in &cards { + for profile_name in card.profiles.keys() { + let name = get_profile_display_name(profile_name, card); + if !name.is_vr { + continue; + } + switch_sink_card(frontend_tasks, card, profile_name, &name)?; + return Ok(()); + } + } + + frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key( + "AUDIO.NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY", + ))); + + Ok(()) +} + +const ONE_HUNDRED_PERCENT: f32 = 100.0; +const VOLUME_MULT: f32 = 1.0 / ONE_HUNDRED_PERCENT; + +impl View { + fn handle_func_button_click(&self, task: ViewTask) -> ButtonClickCallback { + let tasks = self.tasks.clone(); + let on_update = self.on_update.clone(); + Box::new(move |_common, _evt| { + tasks.push(task.clone()); + (*on_update)(); + Ok(()) + }) + } + + pub fn new(params: Params) -> anyhow::Result { + let tasks = ViewTasks::new(); + + let state = wgui::parser::parse_from_assets(&doc_params(¶ms.globals), params.layout, params.parent_id)?; + + let id_devices = state.get_widget_id("devices")?; + + let btn_sinks = state.fetch_component_as::("btn_sinks")?; + let btn_sources = state.fetch_component_as::("btn_sources")?; + let btn_cards = state.fetch_component_as::("btn_cards")?; + let btn_auto = state.fetch_component_as::("btn_auto")?; + + let mut res = Self { + globals: params.globals, + frontend_tasks: params.frontend_tasks, + state, + mode: CurrentMode::Sinks, + id_devices, + tasks, + on_update: params.on_update, + btn_sinks: btn_sinks.clone(), + btn_sources: btn_sources.clone(), + btn_cards: btn_cards.clone(), + }; + + btn_sinks.on_click(res.handle_func_button_click(ViewTask::SetMode(CurrentMode::Sinks))); + btn_sources.on_click(res.handle_func_button_click(ViewTask::SetMode(CurrentMode::Sources))); + btn_cards.on_click(res.handle_func_button_click(ViewTask::SetMode(CurrentMode::Cards))); + btn_auto.on_click(res.handle_func_button_click(ViewTask::AutoSwitch)); + + res.init_mode_sinks(params.layout)?; + res.update_button_highlights(params.layout)?; + + Ok(res) + } + + fn update_button_highlights(&self, layout: &mut Layout) -> anyhow::Result<()> { + let defaults = self.globals.defaults(); + + let mut c = layout.start_common(); + let mut common = c.common(); + + let num: u8 = match &self.mode { + CurrentMode::Sinks => 0, + CurrentMode::Sources => 1, + CurrentMode::Cards => 2, + CurrentMode::CardProfileSelector(_) => 255, + }; + + let mut perform = |btn_num: u8, btn: &Rc| { + btn.set_color( + &mut common, + if num == btn_num { + defaults.accent_color + } else { + defaults.button_color + }, + ); + }; + + perform(0, &self.btn_sinks); + perform(1, &self.btn_sources); + perform(2, &self.btn_cards); + + c.finish()?; + Ok(()) + } + + fn process_tasks(&mut self, layout: &mut Layout) -> anyhow::Result { + let tasks = self.tasks.drain(); + if tasks.is_empty() { + return Ok(false); + } + + let mut set_sink_volume: Option = None; + let mut set_source_volume: Option = None; + + for task in tasks { + match task { + ViewTask::Remount => { + self.update_button_highlights(layout)?; + match &self.mode { + CurrentMode::Sinks => self.init_mode_sinks(layout)?, + CurrentMode::Sources => self.init_mode_sources(layout)?, + CurrentMode::Cards => self.init_mode_cards(layout)?, + CurrentMode::CardProfileSelector(card) => self.init_mode_card_selector(layout, card.clone())?, + } + } + ViewTask::SetSinkVolume(s) => { + set_sink_volume = Some(s); + } + ViewTask::SetSourceVolume(s) => { + set_source_volume = Some(s); + } + ViewTask::SetMode(current_mode) => { + self.mode = current_mode; + self.tasks.push(ViewTask::Remount); + } + ViewTask::SetCardProfile(c) => { + pactl_wrapper::set_card_profile(c.card.index, &c.profile_name)?; + } + ViewTask::AutoSwitch => { + switch_to_vr_microphone(&self.frontend_tasks)?; + switch_to_vr_speakers(&self.frontend_tasks)?; + self.tasks.push(ViewTask::Remount); + } + } + } + + // set volume only to the latest event (prevent cpu time starvation + // due to excessive input motion events) + if let Some(s) = set_sink_volume { + pactl_wrapper::set_sink_volume(s.idx, s.volume)?; + } + + if let Some(s) = set_source_volume { + pactl_wrapper::set_source_volume(s.idx, s.volume)?; + } + + Ok(true) + } + + pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> { + while self.process_tasks(layout)? {} + + Ok(()) + } + + fn mount_card(&mut self, params: MountCardParams) -> anyhow::Result<()> { + let desc = ¶ms.card.properties.device_description; + let disp_name = get_profile_display_name(¶ms.card.active_profile, params.card); + + let mut par = HashMap::, Rc>::new(); + par.insert("card_name".into(), desc.as_str().into()); + par.insert("profile_name".into(), disp_name.name.as_str().into()); + + let data = self + .state + .parse_template(&doc_params(&self.globals), "Card", params.layout, self.id_devices, par)?; + + let btn_card = data.fetch_component_as::("btn_card")?; + btn_card.on_click({ + let tasks = self.tasks.clone(); + let card = params.card.clone(); + let on_update = self.on_update.clone(); + Box::new(move |_common, _evt| { + tasks.push(ViewTask::SetMode(CurrentMode::CardProfileSelector(card.clone()))); + (*on_update)(); + Ok(()) + }) + }); + + log::info!("mount card TODO: {}", params.card.name); + Ok(()) + } + + fn mount_device_slider(&mut self, params: MountDeviceSliderParams) -> anyhow::Result<()> { + let mut par = HashMap::, Rc>::new(); + + if let Some(disp) = ¶ms.disp { + par.insert("device_name".into(), disp.name.as_str().into()); + par.insert("device_icon".into(), disp.icon_path.into()); + } else { + par.insert("device_name".into(), params.alt_desc.into()); + par.insert("device_icon".into(), "dashboard/binary.svg".into()); + } + + par.insert( + "volume_icon".into(), + if params.muted { + "dashboard/volume_off.svg".into() + } else { + "dashboard/volume.svg".into() + }, + ); + + let data = self.state.parse_template( + &doc_params(&self.globals), + "DeviceSlider", + params.layout, + self.id_devices, + par, + )?; + + let mut c = params.layout.start_common(); + let mut common = c.common(); + + let checkbox = data.fetch_component_as::("checkbox")?; + let btn_mute = data.fetch_component_as::("btn_mute")?; + let slider = data.fetch_component_as::("slider")?; + + slider.set_value(&mut common, params.control.on_volume_request()? / VOLUME_MULT); + + checkbox.set_checked(&mut common, params.checked); + + checkbox.on_toggle({ + let control = params.control.clone(); + Box::new(move |_common, _event| { + control.on_check()?; + Ok(()) + }) + }); + + slider.on_value_changed({ + let control = params.control.clone(); + Box::new(move |_common, event| { + control.on_volume_change(event.value * VOLUME_MULT)?; + Ok(()) + }) + }); + + btn_mute.on_click({ + let control = params.control.clone(); + Box::new(move |_common, _event| { + control.on_mute_toggle()?; + Ok(()) + }) + }); + + c.finish()?; + + Ok(()) + } + + fn init_mode_sinks(&mut self, layout: &mut Layout) -> anyhow::Result<()> { + log::info!("refreshing sink list"); + + let sinks = pactl_wrapper::list_sinks()?; + let cards = pactl_wrapper::list_cards()?; + let default_sink = pactl_wrapper::get_default_sink(&sinks)?; + + layout.remove_children(self.id_devices); + + for sink in sinks { + let card = get_card_from_sink(&sink, &cards); + + let checked = if let Some(default_sink) = &default_sink { + sink.index == default_sink.index + } else { + false + }; + + let alt_desc = sink.description.clone(); + let muted = sink.mute; + + let control = Rc::new(ControlSink::new(self.tasks.clone(), self.on_update.clone(), sink)); + + let disp = card + .as_ref() + .map(|card| get_profile_display_name(&card.active_profile, card)); + + self.mount_device_slider(MountDeviceSliderParams { + checked, + disp, + alt_desc, + layout, + control, + muted, + })?; + } + + Ok(()) + } + + fn init_mode_sources(&mut self, layout: &mut Layout) -> anyhow::Result<()> { + log::info!("refreshing source list"); + + let sources = pactl_wrapper::list_sources()?; + let cards = pactl_wrapper::list_cards()?; + let default_source = pactl_wrapper::get_default_source(&sources)?; + + layout.remove_children(self.id_devices); + + for source in sources { + let card = get_card_from_source(&source, &cards); + + let checked = if let Some(default_source) = &default_source { + source.index == default_source.index + } else { + false + }; + + let alt_desc = source.description.clone(); + let muted = source.mute; + + let control = Rc::new(ControlSource::new(self.tasks.clone(), self.on_update.clone(), source)); + + let disp = card + .as_ref() + .map(|card| get_profile_display_name(&card.active_profile, card)); + + self.mount_device_slider(MountDeviceSliderParams { + checked, + disp, + alt_desc, + layout, + control, + muted, + })?; + } + + Ok(()) + } + + fn init_mode_cards(&mut self, layout: &mut Layout) -> anyhow::Result<()> { + log::info!("refreshing card list"); + + let cards = pactl_wrapper::list_cards()?; + layout.remove_children(self.id_devices); + + for card in cards { + self.mount_card(MountCardParams { layout, card: &card })?; + } + + Ok(()) + } + + fn init_mode_card_selector(&mut self, layout: &mut Layout, card: pactl_wrapper::Card) -> anyhow::Result<()> { + log::info!("showing card selector for {}", card.name); + + layout.remove_children(self.id_devices); + + { + let data = self.state.parse_template( + &doc_params(&self.globals), + "SelectAudioProfileText", + layout, + self.id_devices, + Default::default(), + )?; + let btn_back = data.fetch_component_as::("btn_back")?; + btn_back.on_click({ + let tasks = self.tasks.clone(); + let on_update = self.on_update.clone(); + Box::new(move |_, _| { + tasks.push(ViewTask::SetMode(CurrentMode::Cards)); + (*on_update)(); + Ok(()) + }) + }); + } + + let mut cells = Vec::::new(); + for profile_name in card.profiles.keys() { + if profile_name.contains("surround") { + continue; // we aren't interested in that + } + + let disp_name = get_profile_display_name(profile_name, &card); + cells.push(SelectorCell { + key: profile_name.clone(), + display_text: disp_name.name, + icon_path: disp_name.icon_path, + }); + } + + let mut ess = ConstructEssentials { + layout, + parent: self.id_devices, + }; + + mount_multi_selector(MultiSelectorParams { + cells: &cells, + def_cell: &card.active_profile, + ess: &mut ess, + on_click: { + let card = card.clone(); + let tasks = self.tasks.clone(); + let on_update = self.on_update.clone(); + Rc::new(move |profile_name| { + tasks.push(ViewTask::SetCardProfile(CardAndProfileName { + card: card.clone(), + profile_name: profile_name.to_string(), + })); + tasks.push(ViewTask::SetMode(CurrentMode::Cards)); + (*on_update)(); + }) + }, + })?; + + Ok(()) + } +} diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs new file mode 100644 index 0000000..8fe26e0 --- /dev/null +++ b/dash-frontend/src/views/mod.rs @@ -0,0 +1,2 @@ +pub mod app_launcher; +pub mod audio_settings; diff --git a/scripts/translator/bun.lock b/scripts/translator/bun.lock new file mode 100644 index 0000000..8df74b6 --- /dev/null +++ b/scripts/translator/bun.lock @@ -0,0 +1,37 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "llm_translator", + "dependencies": { + "ollama": "^0.5.14", + }, + "devDependencies": { + "@types/bun": "latest", + "@types/node": "^22.13.13", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="], + + "@types/node": ["@types/node@22.17.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ=="], + + "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], + + "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "ollama": ["ollama@0.5.16", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-OEbxxOIUZtdZgOaTPAULo051F5y+Z1vosxEYOoABPnQKeW7i4O8tJNlxCB+xioyoorVqgjkdj+TA1f1Hy2ug/w=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="], + } +} diff --git a/scripts/translator/description.txt b/scripts/translator/description.txt new file mode 100644 index 0000000..1774ac5 --- /dev/null +++ b/scripts/translator/description.txt @@ -0,0 +1,16 @@ +You are a translator for the VR application which translates strings from English to {TARGET_LANG} language. +Info: This program is intended to be used as an utility to easily access your desktop display from within VR. + +Glossary: +- wlx-overlay-s: The name of this software (also called WlxOverlay-S) +- WayVR: A Wayland compositor intended to be used in VR +- WayVR Dashboard: An application (and game) launcher which is displayed in front of the user +- OpenVR: API made by Valve +- OpenXR: API made by Khronos +- OSC: OpenSoundControl +- Playspace: A designated real area where users can interact with the virtual environment +- Space-drag: A feature which allows the user to move their HMD origin position via a controller +- Passthrough: Some headsets have built-in cameras for displaying image on the HMD screen +End of glossary; + +You will be given the input in the code blocks which needs to be translated to {TARGET_LANG} language. Write only the result in codeblocks, do not explain. Keep any newlines and other important formatting-required identifiers as-is. diff --git a/scripts/translator/main.ts b/scripts/translator/main.ts new file mode 100644 index 0000000..134ef76 --- /dev/null +++ b/scripts/translator/main.ts @@ -0,0 +1,189 @@ +import { exit } from "process"; +import * as fsp from "fs/promises"; +import path from "path"; +import * as fs from "fs"; +import ollama from 'ollama' + +const model_name = process.env["MODEL"] as string; +const template_name = process.env["TEMPLATE"] as string; +let lang_path = process.env["LANG_PATH"] as string; + +if (model_name === undefined) { + console.log("MODEL not set"); + exit(-1); +} + +if (template_name === undefined) { + console.log("TEMPLATE not set"); + exit(-1); +} + +if (lang_path === undefined) { + console.log("LANG_PATH is not set. Try one of these:\n\nLANG_PATH=../../uidev/assets/lang/ ./run.sh\nLANG_PATH=../../dash-frontend/assets/lang/ ./run.sh\n"); + exit(-1); +} + +lang_path = path.resolve(__dirname + "/" + lang_path); +if (lang_path === undefined || !fs.existsSync(lang_path)) { + console.log("Invalid or non-existent LANG_PATH"); + exit(-1); +} + +const current_path = path.resolve(__dirname); +const templates_path = path.resolve(__dirname + "/templates"); + +async function loop_object(obj: any, initial_str: string, callback: (key: string, value: string) => Promise) { + for (var key in obj) { + let full_key = initial_str + key; + if (typeof obj[key] === "object" && obj[key] !== null) { + await loop_object(obj[key], full_key + ".", callback) + } else if (obj.hasOwnProperty(key)) { + await callback(full_key, obj[key]) + } + } +} + +function extract_backticks(str: string) { + const regex = /`([^`]+)`/g; + return str.match(regex)?.map(match => match.slice(1, -1).trim()); +} + +function set_i18n_key(obj: any, key: string, value: string | undefined) { + const parts = key.split('.'); + let cur_level = obj; + for (let i = 0; i < parts.length - 1; i++) { + const part = parts[i]!; + if (!cur_level[part]) { + cur_level[part] = {}; + } + cur_level = cur_level[part]; + } + cur_level[parts[parts.length - 1]!] = value; +} + +function key_exists(obj: any, key: string) { + const parts = key.split('.'); + let level = obj; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]!; + if (!level || !level[part]) { + return false; + } + level = level[part]; + } + + return true; +}; + +interface Example { + key: string; + en: string; + translated: string; +} + +interface Template { + full_name: string; // "Polish" + examples: Example[] +} + +function gen_prompt(description: string, template: Template, key: string, english_translation: string) { + let num = 1; + for (const example of template.examples) { + description += "\nExample " + num + ":\n\n"; + description += "Translate key `" + example.key + "` from English to " + template.full_name + ":\n\n"; + description += "```\n"; + description += example.en + "\n"; + description += "```\n\n"; + description += "Result:\n\n"; + description += "```\n"; + description += example.translated + "\n"; + description += "```\n"; + num += 1; + } + description += "\nEnd of examples.\n\n"; + description += "Translate key `" + key + "` from English to " + template.full_name + ":\n\n"; + description += "```\n"; + description += english_translation + "\n"; + description += "```\n"; + return description; +} + +async function run() { + const template = JSON.parse(await fsp.readFile(templates_path + "/" + template_name + ".json", "utf-8")) as Template; + + let description_txt = await fsp.readFile(current_path + "/description.txt", "utf-8"); + description_txt = description_txt.replaceAll("{TARGET_LANG}", template.full_name); + + const orig_english_json = JSON.parse(await fsp.readFile(lang_path + "/en.json") as any); + + let orig_translated_json = {}; + try { + orig_translated_json = JSON.parse((await fsp.readFile(lang_path + "/" + template_name + ".json")).toString()); + } + catch (_e) { } + + let llm_translated_json = {}; + const translated_json_path = lang_path + "/" + template_name + ".json"; + if (await fsp.exists(translated_json_path)) { + llm_translated_json = JSON.parse((await fsp.readFile(translated_json_path)).toString()); + } + + let human = 0; + let llm = 0; + + let total_count = 0; + await loop_object(orig_english_json, "", async () => { + total_count += 1; + }); + + await loop_object(llm_translated_json, "", async (key, _) => { + if (!key_exists(orig_english_json, key)) { + console.log("Removing key", key); + set_i18n_key(llm_translated_json, key, undefined); + fsp.writeFile(translated_json_path, JSON.stringify(llm_translated_json, undefined, 2)); + } + }); + + await loop_object(orig_english_json, "", async (key, english_translation) => { + if (key_exists(orig_translated_json, key)) { + human += 1; + return; + } + + if (key_exists(llm_translated_json, key)) { + llm += 1; + return; + } + + console.log("Translating", key, "..."); + llm++; + + const prompt = gen_prompt(description_txt, template, key, english_translation); + + const response = await ollama.chat({ + model: model_name, + messages: [{ role: "user", content: prompt }], + options: { + seed: 12345, + } + }) + + const msg = extract_backticks(response.message.content); + if (msg === undefined || msg[0] === undefined) { + throw new Error("backticks failed. Raw content: " + response.message.content); + } + + console.log(" >>>", msg); + + set_i18n_key(llm_translated_json, key, msg[0]); + fsp.writeFile(translated_json_path, JSON.stringify(llm_translated_json, undefined, 2)); + }); + + console.log("\"" + template_name + "\" translation finished,", human, "were already human translated,", llm, "llm-translated (" + Math.round((llm / total_count) * 100.0) + "% machine-translated)"); +} + +run().catch((e) => { + console.log("Fatal error:", e); + exit(-1); +}); \ No newline at end of file diff --git a/scripts/translator/package-lock.json b/scripts/translator/package-lock.json new file mode 100644 index 0000000..0834aac --- /dev/null +++ b/scripts/translator/package-lock.json @@ -0,0 +1,108 @@ +{ + "name": "llm_translator", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "llm_translator", + "dependencies": { + "ollama": "^0.5.14" + }, + "devDependencies": { + "@types/bun": "latest", + "@types/node": "^22.13.13" + }, + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/@types/bun": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.2.19.tgz", + "integrity": "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.2.19" + } + }, + "node_modules/@types/node": { + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz", + "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/bun-types": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.2.19.tgz", + "integrity": "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + }, + "peerDependencies": { + "@types/react": "^19" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ollama": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.16.tgz", + "integrity": "sha512-OEbxxOIUZtdZgOaTPAULo051F5y+Z1vosxEYOoABPnQKeW7i4O8tJNlxCB+xioyoorVqgjkdj+TA1f1Hy2ug/w==", + "license": "MIT", + "dependencies": { + "whatwg-fetch": "^3.6.20" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + } + } +} diff --git a/scripts/translator/package.json b/scripts/translator/package.json new file mode 100644 index 0000000..d01cf03 --- /dev/null +++ b/scripts/translator/package.json @@ -0,0 +1,16 @@ +{ + "devDependencies": { + "@types/bun": "latest", + "@types/node": "^22.13.13" + }, + "name": "llm_translator", + "module": "index.ts", + "type": "module", + "private": true, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "ollama": "^0.5.14" + } +} diff --git a/scripts/translator/run.sh b/scripts/translator/run.sh new file mode 100755 index 0000000..93261e6 --- /dev/null +++ b/scripts/translator/run.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +cd "$(dirname "$0")" + +bun install + +export MODEL="gemma3:12b" + +TEMPLATE="pl" bun main.ts +TEMPLATE="de" bun main.ts +TEMPLATE="ja" bun main.ts +TEMPLATE="es" bun main.ts diff --git a/scripts/translator/templates/de.json b/scripts/translator/templates/de.json new file mode 100644 index 0000000..4f617fa --- /dev/null +++ b/scripts/translator/templates/de.json @@ -0,0 +1,20 @@ +{ + "full_name": "German", + "examples": [ + { + "key": "BAR.OPACITY", + "en": "Opacity", + "translated": "Undurchsichtigkeit" + }, + { + "key": "PANEL.CLOSE", + "en": "Close panel", + "translated": "Schließen Sie das Panel" + }, + { + "key": "SETTINGS.LEFT_HANDED_MODE", + "en": "Left-handed mode", + "translated": "Linkshändige Modus" + } + ] +} diff --git a/scripts/translator/templates/es.json b/scripts/translator/templates/es.json new file mode 100644 index 0000000..f2eb766 --- /dev/null +++ b/scripts/translator/templates/es.json @@ -0,0 +1,20 @@ +{ + "full_name": "Spanish", + "examples": [ + { + "key": "BAR.OPACITY", + "en": "Opacity", + "translated": "Opacidad" + }, + { + "key": "PANEL.CLOSE", + "en": "Close panel", + "translated": "Cerrar panel" + }, + { + "key": "SETTINGS.LEFT_HANDED_MODE", + "en": "Left-handed mode", + "translated": "Modo para zurdos" + } + ] +} diff --git a/scripts/translator/templates/ja.json b/scripts/translator/templates/ja.json new file mode 100644 index 0000000..c4e657c --- /dev/null +++ b/scripts/translator/templates/ja.json @@ -0,0 +1,20 @@ +{ + "full_name": "Japanese", + "examples": [ + { + "key": "BAR.OPACITY", + "en": "Opacity", + "translated": "不透明度" + }, + { + "key": "PANEL.CLOSE", + "en": "Close panel", + "translated": "パネルを閉じる" + }, + { + "key": "SETTINGS.LEFT_HANDED_MODE", + "en": "Left-handed mode", + "translated": "左利きモード" + } + ] +} diff --git a/scripts/translator/templates/pl.json b/scripts/translator/templates/pl.json new file mode 100644 index 0000000..6e7866f --- /dev/null +++ b/scripts/translator/templates/pl.json @@ -0,0 +1,20 @@ +{ + "full_name": "Polish", + "examples": [ + { + "key": "BAR.OPACITY", + "en": "Opacity", + "translated": "Przezroczystość" + }, + { + "key": "PANEL.CLOSE", + "en": "Close panel", + "translated": "Zamknij panel" + }, + { + "key": "SETTINGS.LEFT_HANDED_MODE", + "en": "Left-handed mode", + "translated": "Tryb leworęczny" + } + ] +} diff --git a/scripts/translator/tsconfig.json b/scripts/translator/tsconfig.json new file mode 100644 index 0000000..7b72024 --- /dev/null +++ b/scripts/translator/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "lib": ["esnext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/src/backend/common.rs b/src/backend/common.rs deleted file mode 100644 index de399d2..0000000 --- a/src/backend/common.rs +++ /dev/null @@ -1,378 +0,0 @@ -use std::sync::{Arc, LazyLock}; - -#[cfg(feature = "openxr")] -use openxr as xr; - -use glam::{Affine3A, Vec3, Vec3A}; -use idmap::IdMap; -use serde::Deserialize; -use thiserror::Error; - -use crate::{ - config::AStrSetExt, - hid::{get_keymap_wl, get_keymap_x11}, - overlays::{ - anchor::create_anchor, - keyboard::{create_keyboard, KEYBOARD_NAME}, - screen::WlxClientAlias, - watch::{create_watch, WATCH_NAME}, - }, - state::AppState, -}; - -use super::overlay::{OverlayData, OverlayID}; - -#[derive(Error, Debug)] -pub enum BackendError { - #[error("backend not supported")] - NotSupported, - #[cfg(feature = "openxr")] - #[error("OpenXR Error: {0:?}")] - OpenXrError(#[from] xr::sys::Result), - #[error("Shutdown")] - Shutdown, - #[error("Restart")] - Restart, - #[error("Fatal: {0:?}")] - Fatal(#[from] anyhow::Error), -} - -#[cfg(feature = "wayland")] -fn create_wl_client() -> Option { - wlx_capture::wayland::WlxClient::new() -} - -#[cfg(not(feature = "wayland"))] -fn create_wl_client() -> Option { - None -} - -pub struct OverlayContainer -where - T: Default, -{ - overlays: IdMap>, - wl: Option, -} - -impl OverlayContainer -where - T: Default, -{ - pub fn new(app: &mut AppState, headless: bool) -> anyhow::Result { - let mut overlays = IdMap::new(); - let mut show_screens = app.session.config.show_screens.clone(); - let mut wl = None; - let mut keymap = None; - - app.screens.clear(); - - if headless { - log::info!("Running in headless mode; keyboard will be en-US"); - } else { - wl = create_wl_client(); - - let data = if let Some(wl) = wl.as_mut() { - log::info!("Wayland detected."); - keymap = get_keymap_wl() - .map_err(|f| log::warn!("Could not load keyboard layout: {f}")) - .ok(); - crate::overlays::screen::create_screens_wayland(wl, app) - } else { - log::info!("Wayland not detected, assuming X11."); - keymap = get_keymap_x11() - .map_err(|f| log::warn!("Could not load keyboard layout: {f}")) - .ok(); - match crate::overlays::screen::create_screens_x11pw(app) { - Ok(data) => data, - Err(e) => { - log::info!("Will not use X11 PipeWire capture: {e:?}"); - crate::overlays::screen::create_screens_xshm(app)? - } - } - }; - - if show_screens.is_empty() { - if let Some((_, s, _)) = data.screens.first() { - show_screens.arc_set(s.name.clone()); - } - } - - for (meta, mut state, backend) in data.screens { - if show_screens.arc_get(state.name.as_ref()) { - state.show_hide = true; - } - overlays.insert( - state.id.0, - OverlayData:: { - state, - backend, - ..Default::default() - }, - ); - app.screens.push(meta); - } - } - - let anchor = create_anchor(app)?; - overlays.insert(anchor.state.id.0, anchor); - - let mut watch = create_watch::(app)?; - watch.state.want_visible = true; - overlays.insert(watch.state.id.0, watch); - - let mut keyboard = create_keyboard(app, keymap)?; - keyboard.state.show_hide = show_screens.arc_get(KEYBOARD_NAME); - keyboard.state.want_visible = false; - overlays.insert(keyboard.state.id.0, keyboard); - - Ok(Self { overlays, wl }) - } - - #[cfg(not(feature = "wayland"))] - pub fn update(&mut self, _app: &mut AppState) -> anyhow::Result>> { - Ok(vec![]) - } - #[cfg(feature = "wayland")] - #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] - #[allow(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] - pub fn update(&mut self, app: &mut AppState) -> anyhow::Result>> { - use crate::overlays::{ - screen::{create_screen_interaction, create_screen_renderer_wl, load_pw_token_config}, - watch::create_watch_canvas, - }; - use glam::vec2; - use wlx_capture::wayland::OutputChangeEvent; - - let mut removed_overlays = vec![]; - let Some(wl) = self.wl.as_mut() else { - return Ok(removed_overlays); - }; - - wl.dispatch_pending(); - - let mut create_ran = false; - let mut extent_dirty = false; - let mut watch_dirty = false; - - let mut maybe_token_store = None; - - for ev in wl.iter_events().collect::>() { - match ev { - OutputChangeEvent::Create(_) => { - if create_ran { - continue; - } - let data = crate::overlays::screen::create_screens_wayland(wl, app); - create_ran = true; - for (meta, state, backend) in data.screens { - self.overlays.insert( - state.id.0, - OverlayData:: { - state, - backend, - ..Default::default() - }, - ); - app.screens.push(meta); - watch_dirty = true; - } - } - OutputChangeEvent::Destroy(id) => { - let Some(idx) = app.screens.iter().position(|s| s.native_handle == id) else { - continue; - }; - - let meta = &app.screens[idx]; - let removed = self.overlays.remove(meta.id.0).unwrap(); - removed_overlays.push(removed); - log::info!("{}: Destroyed", meta.name); - app.screens.remove(idx); - watch_dirty = true; - extent_dirty = true; - } - OutputChangeEvent::Logical(id) => { - let Some(meta) = app.screens.iter().find(|s| s.native_handle == id) else { - continue; - }; - let output = wl.outputs.get(id).unwrap(); - let Some(overlay) = self.overlays.get_mut(meta.id.0) else { - continue; - }; - let logical_pos = - vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32); - let logical_size = - vec2(output.logical_size.0 as f32, output.logical_size.1 as f32); - let transform = output.transform.into(); - overlay - .backend - .set_interaction(Box::new(create_screen_interaction( - logical_pos, - logical_size, - transform, - ))); - extent_dirty = true; - } - OutputChangeEvent::Physical(id) => { - let Some(meta) = app.screens.iter().find(|s| s.native_handle == id) else { - continue; - }; - let output = wl.outputs.get(id).unwrap(); - let Some(overlay) = self.overlays.get_mut(meta.id.0) else { - continue; - }; - - let has_wlr_dmabuf = wl.maybe_wlr_dmabuf_mgr.is_some(); - let has_wlr_screencopy = wl.maybe_wlr_screencopy_mgr.is_some(); - - let pw_token_store = maybe_token_store.get_or_insert_with(|| { - load_pw_token_config().unwrap_or_else(|e| { - log::warn!("Failed to load PipeWire token config: {:?}", e); - Default::default() - }) - }); - - if let Some(renderer) = create_screen_renderer_wl( - output, - has_wlr_dmabuf, - has_wlr_screencopy, - pw_token_store, - &app, - ) { - overlay.backend.set_renderer(Box::new(renderer)); - } - extent_dirty = true; - } - } - } - - if extent_dirty && !create_ran { - let extent = wl.get_desktop_extent(); - let origin = wl.get_desktop_origin(); - app.hid_provider - .set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32)); - app.hid_provider - .set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32)); - } - - if watch_dirty { - let watch = self.mut_by_name(WATCH_NAME).unwrap(); // want panic - match create_watch_canvas(None, app) { - Ok(canvas) => { - watch.backend = Box::new(canvas); - } - Err(e) => { - log::error!("Failed to create watch canvas: {}", e); - } - } - } - - Ok(removed_overlays) - } - - pub fn mut_by_selector(&mut self, selector: &OverlaySelector) -> Option<&mut OverlayData> { - match selector { - OverlaySelector::Id(id) => self.mut_by_id(*id), - OverlaySelector::Name(name) => self.mut_by_name(name), - } - } - - pub fn remove_by_selector(&mut self, selector: &OverlaySelector) -> Option> { - match selector { - OverlaySelector::Id(id) => self.overlays.remove(id.0), - OverlaySelector::Name(name) => { - let id = self - .overlays - .iter() - .find(|(_, o)| *o.state.name == **name) - .map(|(id, _)| *id); - id.and_then(|id| self.overlays.remove(id)) - } - } - } - - pub fn get_by_id(&mut self, id: OverlayID) -> Option<&OverlayData> { - self.overlays.get(id.0) - } - - pub fn mut_by_id(&mut self, id: OverlayID) -> Option<&mut OverlayData> { - self.overlays.get_mut(id.0) - } - - pub fn get_by_name<'a>(&'a mut self, name: &str) -> Option<&'a OverlayData> { - self.overlays.values().find(|o| *o.state.name == *name) - } - - pub fn mut_by_name<'a>(&'a mut self, name: &str) -> Option<&'a mut OverlayData> { - self.overlays.values_mut().find(|o| *o.state.name == *name) - } - - pub fn iter(&self) -> impl Iterator> { - self.overlays.values() - } - - pub fn iter_mut(&mut self) -> impl Iterator> { - self.overlays.values_mut() - } - - pub fn add(&mut self, overlay: OverlayData) { - self.overlays.insert(overlay.state.id.0, overlay); - } - - pub fn show_hide(&mut self, app: &mut AppState) { - let any_shown = self - .overlays - .values() - .any(|o| o.state.show_hide && o.state.want_visible); - - if !any_shown { - static ANCHOR_LOCAL: LazyLock = - LazyLock::new(|| Affine3A::from_translation(Vec3::NEG_Z)); - let hmd = snap_upright(app.input_state.hmd, Vec3A::Y); - app.anchor = hmd * *ANCHOR_LOCAL; - } - - self.overlays.values_mut().for_each(|o| { - if o.state.show_hide { - o.state.want_visible = !any_shown; - if o.state.want_visible - && app.session.config.realign_on_showhide - && o.state.recenter - { - o.state.reset(app, false); - } - } - // toggle watch back on if it was hidden - if !any_shown && *o.state.name == *WATCH_NAME { - o.state.reset(app, true); - } - }); - } -} - -#[derive(Clone, Deserialize, Debug)] -#[serde(untagged)] -pub enum OverlaySelector { - Id(OverlayID), - Name(Arc), -} - -pub fn snap_upright(transform: Affine3A, up_dir: Vec3A) -> Affine3A { - if transform.x_axis.dot(up_dir).abs() < 0.2 { - let scale = transform.x_axis.length(); - let col_z = transform.z_axis.normalize(); - let col_y = up_dir; - let col_x = col_y.cross(col_z); - let col_y = col_z.cross(col_x).normalize(); - let col_x = col_x.normalize(); - - Affine3A::from_cols( - col_x * scale, - col_y * scale, - col_z * scale, - transform.translation, - ) - } else { - transform - } -} diff --git a/src/backend/input.rs b/src/backend/input.rs deleted file mode 100644 index 2838703..0000000 --- a/src/backend/input.rs +++ /dev/null @@ -1,744 +0,0 @@ -use std::f32::consts::PI; -use std::process::{Child, Command}; -use std::{collections::VecDeque, time::Instant}; - -use glam::{Affine3A, Vec2, Vec3, Vec3A, Vec3Swizzles}; - -use smallvec::{smallvec, SmallVec}; - -use crate::backend::common::OverlaySelector; -use crate::backend::overlay::Positioning; -use crate::config::AStrMapExt; -use crate::overlays::anchor::ANCHOR_NAME; -use crate::state::{AppSession, AppState, KeyboardFocus}; - -use super::overlay::{OverlayID, OverlayState}; -use super::task::{TaskContainer, TaskType}; -use super::{common::OverlayContainer, overlay::OverlayData}; - -pub struct TrackedDevice { - pub soc: Option, - pub charging: bool, - pub role: TrackedDeviceRole, -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TrackedDeviceRole { - None, - Hmd, - LeftHand, - RightHand, - Tracker, -} - -pub struct InputState { - pub hmd: Affine3A, - pub ipd: f32, - pub pointers: [Pointer; 2], - pub devices: Vec, - processes: Vec, -} - -impl InputState { - pub fn new() -> Self { - Self { - hmd: Affine3A::IDENTITY, - ipd: 0.0, - pointers: [Pointer::new(0), Pointer::new(1)], - devices: Vec::new(), - processes: Vec::new(), - } - } - - pub const fn pre_update(&mut self) { - self.pointers[0].before = self.pointers[0].now; - self.pointers[1].before = self.pointers[1].now; - } - - pub fn post_update(&mut self, session: &AppSession) { - for hand in &mut self.pointers { - #[cfg(debug_assertions)] - debug_print_hand(hand); - - if hand.now.click { - hand.last_click = Instant::now(); - } - - if hand.now.click_modifier_right { - hand.interaction.mode = PointerMode::Right; - continue; - } - - if hand.now.click_modifier_middle { - hand.interaction.mode = PointerMode::Middle; - continue; - } - - let hmd_up = self.hmd.transform_vector3a(Vec3A::Y); - let dot = hmd_up.dot(hand.pose.transform_vector3a(Vec3A::X)) - * 2.0f32.mul_add(-(hand.idx as f32), 1.0); - - hand.interaction.mode = if dot < -0.85 { - PointerMode::Right - } else if dot > 0.7 { - PointerMode::Middle - } else { - PointerMode::Left - }; - - let middle_click_orientation = false; - let right_click_orientation = false; - match hand.interaction.mode { - PointerMode::Middle => { - if !middle_click_orientation { - hand.interaction.mode = PointerMode::Left; - } - } - PointerMode::Right => { - if !right_click_orientation { - hand.interaction.mode = PointerMode::Left; - } - } - _ => {} - } - - if hand.now.alt_click != hand.before.alt_click { - // Reap previous processes - self.processes - .retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_)))); - - let mut args = if hand.now.alt_click { - session.config.alt_click_down.iter() - } else { - session.config.alt_click_up.iter() - }; - - if let Some(program) = args.next() { - if let Ok(child) = Command::new(program).args(args).spawn() { - self.processes.push(child); - } - } - } - } - } -} - -#[cfg(debug_assertions)] -fn debug_print_hand(hand: &Pointer) { - { - if hand.now.click != hand.before.click { - log::debug!("Hand {}: click {}", hand.idx, hand.now.click); - } - if hand.now.grab != hand.before.grab { - log::debug!("Hand {}: grab {}", hand.idx, hand.now.grab); - } - if hand.now.alt_click != hand.before.alt_click { - log::debug!("Hand {}: alt_click {}", hand.idx, hand.now.alt_click); - } - if hand.now.show_hide != hand.before.show_hide { - log::debug!("Hand {}: show_hide {}", hand.idx, hand.now.show_hide); - } - if hand.now.toggle_dashboard != hand.before.toggle_dashboard { - log::debug!( - "Hand {}: toggle_dashboard {}", - hand.idx, - hand.now.toggle_dashboard - ); - } - if hand.now.space_drag != hand.before.space_drag { - log::debug!("Hand {}: space_drag {}", hand.idx, hand.now.space_drag); - } - if hand.now.space_rotate != hand.before.space_rotate { - log::debug!("Hand {}: space_rotate {}", hand.idx, hand.now.space_rotate); - } - if hand.now.space_reset != hand.before.space_reset { - log::debug!("Hand {}: space_reset {}", hand.idx, hand.now.space_reset); - } - if hand.now.click_modifier_right != hand.before.click_modifier_right { - log::debug!( - "Hand {}: click_modifier_right {}", - hand.idx, - hand.now.click_modifier_right - ); - } - if hand.now.click_modifier_middle != hand.before.click_modifier_middle { - log::debug!( - "Hand {}: click_modifier_middle {}", - hand.idx, - hand.now.click_modifier_middle - ); - } - } -} - -pub struct InteractionState { - pub mode: PointerMode, - pub grabbed: Option, - pub clicked_id: Option, - pub hovered_id: Option, - pub release_actions: VecDeque>, - pub next_push: Instant, - pub haptics: Option, -} - -impl Default for InteractionState { - fn default() -> Self { - Self { - mode: PointerMode::Left, - grabbed: None, - clicked_id: None, - hovered_id: None, - release_actions: VecDeque::new(), - next_push: Instant::now(), - haptics: None, - } - } -} - -pub struct Pointer { - pub idx: usize, - pub pose: Affine3A, - pub raw_pose: Affine3A, - pub now: PointerState, - pub before: PointerState, - pub last_click: Instant, - pub(super) interaction: InteractionState, -} - -impl Pointer { - pub fn new(idx: usize) -> Self { - debug_assert!(idx == 0 || idx == 1); - Self { - idx, - pose: Affine3A::IDENTITY, - raw_pose: Affine3A::IDENTITY, - now: PointerState::default(), - before: PointerState::default(), - last_click: Instant::now(), - interaction: InteractionState::default(), - } - } -} - -#[derive(Clone, Copy, Default)] -pub struct PointerState { - pub scroll_x: f32, - pub scroll_y: f32, - pub click: bool, - pub grab: bool, - pub alt_click: bool, - pub show_hide: bool, - pub toggle_dashboard: bool, - pub space_drag: bool, - pub space_rotate: bool, - pub space_reset: bool, - pub click_modifier_right: bool, - pub click_modifier_middle: bool, - pub move_mouse: bool, -} - -#[derive(Debug, Clone, Copy, Default)] -pub struct PointerHit { - pub pointer: usize, - pub overlay: OverlayID, - pub mode: PointerMode, - pub primary: bool, - pub uv: Vec2, - pub dist: f32, -} - -#[derive(Clone)] -pub struct Haptics { - pub intensity: f32, - pub duration: f32, - pub frequency: f32, -} - -pub trait InteractionHandler { - fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option; - fn on_left(&mut self, app: &mut AppState, pointer: usize); - fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool); - fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32); -} - -pub struct DummyInteractionHandler; - -impl InteractionHandler for DummyInteractionHandler { - fn on_left(&mut self, _app: &mut AppState, _pointer: usize) {} - fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) -> Option { - None - } - fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {} - fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta_y: f32, _delta_x: f32) {} -} - -#[derive(Debug, Clone, Copy, Default)] -struct RayHit { - overlay: OverlayID, - global_pos: Vec3A, - local_pos: Vec2, - dist: f32, -} - -#[derive(Debug, Clone, Copy, Default)] -pub struct GrabData { - pub offset: Vec3A, - pub grabbed_id: OverlayID, - pub old_curvature: Option, - pub grab_all: bool, -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy, Default)] -pub enum PointerMode { - #[default] - Left, - Right, - Middle, - Special, -} - -fn update_focus(focus: &mut KeyboardFocus, state: &OverlayState) { - if let Some(f) = &state.keyboard_focus { - if *focus != *f { - log::info!("Setting keyboard focus to {:?}", *f); - *focus = *f; - } - } -} - -pub fn interact( - overlays: &mut OverlayContainer, - app: &mut AppState, -) -> [(f32, Option); 2] -where - O: Default, -{ - if app.input_state.pointers[1].last_click > app.input_state.pointers[0].last_click { - let right = interact_hand(1, overlays, app); - let left = interact_hand(0, overlays, app); - [left, right] - } else { - let left = interact_hand(0, overlays, app); - let right = interact_hand(1, overlays, app); - [left, right] - } -} - -#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] -fn interact_hand( - idx: usize, - overlays: &mut OverlayContainer, - app: &mut AppState, -) -> (f32, Option) -where - O: Default, -{ - let mut pointer = &mut app.input_state.pointers[idx]; - if let Some(grab_data) = pointer.interaction.grabbed { - if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) { - Pointer::handle_grabbed(idx, grabbed, app); - } else { - log::warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id.0); - pointer.interaction.grabbed = None; - } - return (0.1, None); - } - - let Some(mut hit) = pointer.get_nearest_hit(overlays) else { - if let Some(hovered_id) = pointer.interaction.hovered_id.take() { - if let Some(hovered) = overlays.mut_by_id(hovered_id) { - hovered.backend.on_left(app, idx); - } - pointer = &mut app.input_state.pointers[idx]; - pointer.interaction.hovered_id = None; - } - if !pointer.now.click && pointer.before.click { - if let Some(clicked_id) = pointer.interaction.clicked_id.take() { - if let Some(clicked) = overlays.mut_by_id(clicked_id) { - let hit = PointerHit { - pointer: pointer.idx, - overlay: clicked_id, - mode: pointer.interaction.mode, - ..Default::default() - }; - clicked.backend.on_pointer(app, &hit, false); - } - } - } - return (0.0, None); // no hit - }; - - if let Some(hovered_id) = pointer.interaction.hovered_id { - if hovered_id != hit.overlay { - if let Some(old_hovered) = overlays.mut_by_id(hovered_id) { - if Some(pointer.idx) == old_hovered.primary_pointer { - old_hovered.primary_pointer = None; - } - old_hovered.backend.on_left(app, idx); - pointer = &mut app.input_state.pointers[idx]; - } - } - } - let Some(hovered) = overlays.mut_by_id(hit.overlay) else { - log::warn!("Hit overlay {} does not exist", hit.overlay.0); - return (0.0, None); // no hit - }; - - pointer.interaction.hovered_id = Some(hit.overlay); - - if let Some(primary_pointer) = hovered.primary_pointer { - if hit.pointer <= primary_pointer { - hovered.primary_pointer = Some(hit.pointer); - hit.primary = true; - } - } else { - hovered.primary_pointer = Some(hit.pointer); - hit.primary = true; - } - - #[cfg(debug_assertions)] - log::trace!("Hit: {} {:?}", hovered.state.name, hit); - - if pointer.now.grab && !pointer.before.grab && hovered.state.grabbable { - update_focus(&mut app.keyboard_focus, &hovered.state); - pointer.start_grab(hovered, &mut app.tasks); - return ( - hit.dist, - Some(Haptics { - intensity: 0.25, - duration: 0.1, - frequency: 0.1, - }), - ); - } - - // Pass mouse motion events only if not scrolling - // (allows scrolling on all Chromium-based applications) - let haptics = hovered.backend.on_hover(app, &hit); - - pointer = &mut app.input_state.pointers[idx]; - - if pointer.now.scroll_x.abs() > 0.1 || pointer.now.scroll_y.abs() > 0.1 { - let scroll_x = pointer.now.scroll_x; - let scroll_y = pointer.now.scroll_y; - if app.input_state.pointers[1 - idx] - .interaction - .grabbed - .is_some_and(|x| x.grabbed_id == hit.overlay) - { - let can_curve = hovered - .frame_meta() - .is_some_and(|e| e.extent[0] >= e.extent[1]); - - if can_curve { - let cur = hovered.state.curvature.unwrap_or(0.0); - let new = scroll_y.mul_add(-0.01, cur).min(0.5); - if new <= f32::EPSILON { - hovered.state.curvature = None; - } else { - hovered.state.curvature = Some(new); - } - } else { - hovered.state.curvature = None; - } - } else { - hovered.backend.on_scroll(app, &hit, scroll_y, scroll_x); - } - pointer = &mut app.input_state.pointers[idx]; - } - - if pointer.now.click && !pointer.before.click { - pointer.interaction.clicked_id = Some(hit.overlay); - update_focus(&mut app.keyboard_focus, &hovered.state); - hovered.backend.on_pointer(app, &hit, true); - } else if !pointer.now.click && pointer.before.click { - if let Some(clicked_id) = pointer.interaction.clicked_id.take() { - if let Some(clicked) = overlays.mut_by_id(clicked_id) { - clicked.backend.on_pointer(app, &hit, false); - } - } else { - hovered.backend.on_pointer(app, &hit, false); - } - } - (hit.dist, haptics) -} - -impl Pointer { - fn get_nearest_hit(&mut self, overlays: &mut OverlayContainer) -> Option - where - O: Default, - { - let mut hits: SmallVec<[RayHit; 8]> = smallvec!(); - - for overlay in overlays.iter() { - if !overlay.state.want_visible || !overlay.state.interactable { - continue; - } - - if let Some(hit) = self.ray_test( - overlay.state.id, - &overlay.state.transform, - overlay.state.curvature.as_ref(), - ) { - if hit.dist.is_infinite() || hit.dist.is_nan() { - continue; - } - hits.push(hit); - } - } - - hits.sort_by(|a, b| a.dist.total_cmp(&b.dist)); - - for hit in &hits { - let overlay = overlays.get_by_id(hit.overlay).unwrap(); // safe because we just got the id from the overlay - - let uv = overlay - .state - .interaction_transform - .transform_point2(hit.local_pos); - - if uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 { - continue; - } - - return Some(PointerHit { - pointer: self.idx, - overlay: hit.overlay, - mode: self.interaction.mode, - primary: false, - uv, - dist: hit.dist, - }); - } - - None - } - - fn start_grab(&mut self, overlay: &mut OverlayData, tasks: &mut TaskContainer) - where - O: Default, - { - let offset = self - .pose - .inverse() - .transform_point3a(overlay.state.transform.translation); - - self.interaction.grabbed = Some(GrabData { - offset, - grabbed_id: overlay.state.id, - old_curvature: overlay.state.curvature, - grab_all: matches!(self.interaction.mode, PointerMode::Right), - }); - overlay.state.positioning = match overlay.state.positioning { - Positioning::FollowHand { hand, lerp } => Positioning::FollowHandPaused { hand, lerp }, - Positioning::FollowHead { lerp } => Positioning::FollowHeadPaused { lerp }, - x => x, - }; - - // Show anchor - tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(ANCHOR_NAME.clone()), - Box::new(|app, o| { - o.transform = app.anchor - * Affine3A::from_scale_rotation_translation( - Vec3::ONE * o.spawn_scale, - o.spawn_rotation, - o.spawn_point.into(), - ); - o.dirty = true; - o.want_visible = true; - }), - )); - log::info!("Hand {}: grabbed {}", self.idx, overlay.state.name); - } - - fn handle_grabbed(idx: usize, overlay: &mut OverlayData, app: &mut AppState) - where - O: Default, - { - let mut pointer = &mut app.input_state.pointers[idx]; - if pointer.now.grab { - if let Some(grab_data) = pointer.interaction.grabbed.as_mut() { - if pointer.now.click { - pointer.interaction.mode = PointerMode::Special; - let cur_scale = overlay.state.transform.x_axis.length(); - if cur_scale < 0.1 && pointer.now.scroll_y > 0.0 { - return; - } - if cur_scale > 20. && pointer.now.scroll_y < 0.0 { - return; - } - - overlay.state.transform.matrix3 = overlay - .state - .transform - .matrix3 - .mul_scalar(0.025f32.mul_add(-pointer.now.scroll_y, 1.0)); - } else if app.session.config.allow_sliding && pointer.now.scroll_y.is_finite() { - grab_data.offset.z -= pointer.now.scroll_y * 0.05; - } - overlay.state.transform.translation = - pointer.pose.transform_point3a(grab_data.offset); - overlay.state.realign(&app.input_state.hmd); - overlay.state.dirty = true; - } else { - log::error!("Grabbed overlay {} does not exist", overlay.state.id.0); - pointer.interaction.grabbed = None; - } - } else { - overlay.state.positioning = match overlay.state.positioning { - Positioning::FollowHandPaused { hand, lerp } => { - Positioning::FollowHand { hand, lerp } - } - Positioning::FollowHeadPaused { lerp } => Positioning::FollowHead { lerp }, - x => x, - }; - - let save_success = overlay.state.save_transform(app); - - // re-borrow - pointer = &mut app.input_state.pointers[idx]; - - if save_success { - if let Some(grab_data) = pointer.interaction.grabbed.as_ref() { - if overlay.state.curvature != grab_data.old_curvature { - if let Some(val) = overlay.state.curvature { - app.session - .config - .curve_values - .arc_set(overlay.state.name.clone(), val); - } else { - let ref_name = overlay.state.name.as_ref(); - app.session.config.curve_values.arc_rm(ref_name); - } - } - } - app.session.config.transform_values.arc_set( - overlay.state.name.clone(), - overlay.state.saved_transform.unwrap(), // safe - ); - } - - pointer.interaction.grabbed = None; - - // Hide anchor - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(ANCHOR_NAME.clone()), - Box::new(|_app, o| { - o.want_visible = false; - }), - )); - log::info!("Hand {}: dropped {}", idx, overlay.state.name); - } - } - - fn ray_test( - &self, - overlay: OverlayID, - transform: &Affine3A, - curvature: Option<&f32>, - ) -> Option { - let (dist, local_pos) = curvature.map_or_else( - || { - Some(raycast_plane( - &self.pose, - Vec3A::NEG_Z, - transform, - Vec3A::NEG_Z, - )) - }, - |curvature| raycast_cylinder(&self.pose, Vec3A::NEG_Z, transform, *curvature), - )?; - - if dist < 0.0 { - // hit is behind us - return None; - } - - Some(RayHit { - overlay, - global_pos: self.pose.transform_point3a(Vec3A::NEG_Z * dist), - local_pos, - dist, - }) - } -} - -fn raycast_plane( - source: &Affine3A, - source_fwd: Vec3A, - plane: &Affine3A, - plane_norm: Vec3A, -) -> (f32, Vec2) { - let plane_normal = plane.transform_vector3a(plane_norm); - let ray_dir = source.transform_vector3a(source_fwd); - - let d = plane.translation.dot(-plane_normal); - let dist = -(d + source.translation.dot(plane_normal)) / ray_dir.dot(plane_normal); - - let hit_local = plane - .inverse() - .transform_point3a(source.translation + ray_dir * dist) - .xy(); - - (dist, hit_local) -} - -fn raycast_cylinder( - source: &Affine3A, - source_fwd: Vec3A, - plane: &Affine3A, - curvature: f32, -) -> Option<(f32, Vec2)> { - // this is solved locally; (0,0) is the center of the cylinder, and the cylinder is aligned with the Y axis - let size = plane.x_axis.length(); - let to_local = Affine3A { - matrix3: plane.matrix3.mul_scalar(1.0 / size), - translation: plane.translation, - } - .inverse(); - - let radius = size / (2.0 * PI * curvature); - - let ray_dir = to_local.transform_vector3a(source.transform_vector3a(source_fwd)); - let ray_origin = to_local.transform_point3a(source.translation) + Vec3A::NEG_Z * radius; - - let v_dir = ray_dir.xz(); - let v_pos = ray_origin.xz(); - - let l_dir = v_dir.dot(v_dir); - let l_pos = v_dir.dot(v_pos); - let c = radius.mul_add(-radius, v_pos.dot(v_pos)); - - let d = l_pos.mul_add(l_pos, -(l_dir * c)); - if d < f32::EPSILON { - return None; - } - - let sqrt_d = d.sqrt(); - - let t1 = (-l_pos - sqrt_d) / l_dir; - let t2 = (-l_pos + sqrt_d) / l_dir; - - let t = t1.max(t2); - - if t < f32::EPSILON { - return None; - } - - let mut hit_local = ray_origin + ray_dir * t; - if hit_local.z > 0.0 { - // hitting the opposite half of the cylinder - return None; - } - - let max_angle = 2.0 * (size / (2.0 * radius)); - let x_angle = (hit_local.x / radius).asin(); - - hit_local.x = x_angle / max_angle; - hit_local.y /= size; - - Some((t, hit_local.xy())) -} diff --git a/src/backend/mod.rs b/src/backend/mod.rs deleted file mode 100644 index fa5397a..0000000 --- a/src/backend/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -pub mod common; -pub mod input; -pub mod notifications; - -#[allow(clippy::all)] -mod notifications_dbus; - -#[cfg(feature = "openvr")] -pub mod openvr; - -#[cfg(feature = "openxr")] -pub mod openxr; - -#[cfg(feature = "uidev")] -pub mod uidev; - -#[cfg(feature = "osc")] -pub mod osc; - -#[cfg(feature = "wayvr")] -pub mod wayvr; - -pub mod overlay; - -pub mod task; diff --git a/src/backend/openvr/lines.rs b/src/backend/openvr/lines.rs deleted file mode 100644 index 1c590e6..0000000 --- a/src/backend/openvr/lines.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::f32::consts::PI; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; - -use glam::{Affine3A, Vec3, Vec3A, Vec4}; -use idmap::IdMap; -use ovr_overlay::overlay::OverlayManager; -use ovr_overlay::sys::ETrackingUniverseOrigin; -use vulkano::command_buffer::CommandBufferUsage; -use vulkano::format::Format; -use vulkano::image::view::ImageView; -use vulkano::image::ImageLayout; - -use crate::backend::overlay::{ - FrameMeta, OverlayData, OverlayRenderer, OverlayState, ShouldRender, SplitOverlayBackend, - Z_ORDER_LINES, -}; -use crate::graphics::{CommandBuffers, WlxGraphics}; -use crate::state::AppState; - -use super::overlay::OpenVrOverlayData; - -static LINE_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1); - -pub(super) struct LinePool { - lines: IdMap>, - view: Arc, - colors: [Vec4; 5], -} - -impl LinePool { - pub fn new(graphics: Arc) -> anyhow::Result { - let mut command_buffer = graphics.create_uploads_command_buffer( - graphics.transfer_queue.clone(), - CommandBufferUsage::OneTimeSubmit, - )?; - - let buf = vec![255; 16]; - - let texture = command_buffer.texture2d_raw(2, 2, Format::R8G8B8A8_UNORM, &buf)?; - command_buffer.build_and_execute_now()?; - - graphics - .transition_layout( - texture.clone(), - ImageLayout::ShaderReadOnlyOptimal, - ImageLayout::TransferSrcOptimal, - )? - .wait(None)?; - - let view = ImageView::new_default(texture)?; - - Ok(Self { - lines: IdMap::new(), - view, - colors: [ - Vec4::new(1., 1., 1., 1.), - Vec4::new(0., 0.375, 0.5, 1.), - Vec4::new(0.69, 0.188, 0., 1.), - Vec4::new(0.375, 0., 0.5, 1.), - Vec4::new(1., 0., 0., 1.), - ], - }) - } - - pub fn allocate(&mut self) -> usize { - let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed); - - let mut data = OverlayData:: { - state: OverlayState { - name: Arc::from(format!("wlx-line{id}")), - show_hide: true, - ..Default::default() - }, - backend: Box::new(SplitOverlayBackend { - renderer: Box::new(StaticRenderer { - view: self.view.clone(), - }), - ..Default::default() - }), - data: OpenVrOverlayData { - width: 0.002, - override_width: true, - image_view: Some(self.view.clone()), - image_dirty: true, - ..Default::default() - }, - ..Default::default() - }; - data.state.z_order = Z_ORDER_LINES; - data.state.dirty = true; - - self.lines.insert(id, data); - id - } - - pub fn draw_from( - &mut self, - id: usize, - mut from: Affine3A, - len: f32, - color: usize, - hmd: &Affine3A, - ) { - let rotation = Affine3A::from_axis_angle(Vec3::X, -PI * 0.5); - - from.translation += from.transform_vector3a(Vec3A::NEG_Z) * (len * 0.5); - let mut transform = from * rotation * Affine3A::from_scale(Vec3::new(1., len / 0.002, 1.)); - - let to_hmd = hmd.translation - from.translation; - let sides = [Vec3A::Z, Vec3A::X, Vec3A::NEG_Z, Vec3A::NEG_X]; - let rotations = [ - Affine3A::IDENTITY, - Affine3A::from_axis_angle(Vec3::Y, PI * 0.5), - Affine3A::from_axis_angle(Vec3::Y, PI * 1.0), - Affine3A::from_axis_angle(Vec3::Y, PI * 1.5), - ]; - let mut closest = (0, 0.0); - for (i, &side) in sides.iter().enumerate() { - let dot = to_hmd.dot(transform.transform_vector3a(side)); - if i == 0 || dot > closest.1 { - closest = (i, dot); - } - } - - transform *= rotations[closest.0]; - - debug_assert!(color < self.colors.len()); - - self.draw_transform(id, transform, self.colors[color]); - } - - fn draw_transform(&mut self, id: usize, transform: Affine3A, color: Vec4) { - if let Some(data) = self.lines.get_mut(id) { - data.state.want_visible = true; - data.state.transform = transform; - data.data.color = color; - } else { - log::warn!("Line {id} does not exist"); - } - } - - pub fn update( - &mut self, - universe: ETrackingUniverseOrigin, - overlay: &mut OverlayManager, - app: &mut AppState, - ) -> anyhow::Result<()> { - for data in self.lines.values_mut() { - data.after_input(overlay, app)?; - if data.state.want_visible { - if data.state.dirty { - data.upload_texture(overlay, &app.graphics); - data.state.dirty = false; - } - - data.upload_transform(universe.clone(), overlay); - data.upload_color(overlay); - } - } - Ok(()) - } - - pub fn mark_dirty(&mut self) { - for data in self.lines.values_mut() { - data.state.dirty = true; - } - } -} - -struct StaticRenderer { - view: Arc, -} - -impl OverlayRenderer for StaticRenderer { - fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result { - Ok(ShouldRender::Unable) - } - fn render( - &mut self, - _app: &mut AppState, - _tgt: Arc, - _buf: &mut CommandBuffers, - _alpha: f32, - ) -> anyhow::Result { - Ok(false) - } - fn frame_meta(&mut self) -> Option { - Some(FrameMeta { - extent: self.view.image().extent(), - ..Default::default() - }) - } -} diff --git a/src/backend/overlay.rs b/src/backend/overlay.rs deleted file mode 100644 index d96614e..0000000 --- a/src/backend/overlay.rs +++ /dev/null @@ -1,462 +0,0 @@ -use std::{ - f32::consts::PI, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, - }, -}; - -use anyhow::Ok; -use glam::{Affine2, Affine3A, Mat3A, Quat, Vec2, Vec3, Vec3A}; -use serde::Deserialize; -use vulkano::{format::Format, image::view::ImageView}; - -use crate::{ - config::AStrMapExt, - graphics::CommandBuffers, - state::{AppState, KeyboardFocus}, -}; - -use super::{ - common::snap_upright, - input::{DummyInteractionHandler, Haptics, InteractionHandler, PointerHit}, -}; - -static OVERLAY_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0); - -pub trait OverlayBackend: OverlayRenderer + InteractionHandler { - fn set_renderer(&mut self, renderer: Box); - fn set_interaction(&mut self, interaction: Box); -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Default)] -pub struct OverlayID(pub usize); - -pub const Z_ORDER_TOAST: u32 = 70; -pub const Z_ORDER_LINES: u32 = 69; -pub const Z_ORDER_WATCH: u32 = 68; -pub const Z_ORDER_ANCHOR: u32 = 67; -pub const Z_ORDER_DEFAULT: u32 = 0; -pub const Z_ORDER_DASHBOARD: u32 = Z_ORDER_DEFAULT; - -pub struct OverlayState { - pub id: OverlayID, - pub name: Arc, - pub want_visible: bool, - pub show_hide: bool, - pub grabbable: bool, - pub interactable: bool, - pub recenter: bool, - pub keyboard_focus: Option, - pub dirty: bool, - pub alpha: f32, - pub z_order: u32, - pub transform: Affine3A, - pub spawn_scale: f32, // aka width - pub spawn_point: Vec3A, - pub spawn_rotation: Quat, - pub saved_transform: Option, - pub positioning: Positioning, - pub curvature: Option, - pub interaction_transform: Affine2, - pub birthframe: usize, -} - -impl Default for OverlayState { - fn default() -> Self { - Self { - id: OverlayID(OVERLAY_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed)), - name: Arc::from(""), - want_visible: false, - show_hide: false, - grabbable: false, - recenter: false, - interactable: false, - keyboard_focus: None, - dirty: true, - alpha: 1.0, - z_order: Z_ORDER_DEFAULT, - positioning: Positioning::Floating, - curvature: None, - spawn_scale: 1.0, - spawn_point: Vec3A::NEG_Z, - spawn_rotation: Quat::IDENTITY, - saved_transform: None, - transform: Affine3A::IDENTITY, - interaction_transform: Affine2::IDENTITY, - birthframe: 0, - } - } -} - -pub struct OverlayData -where - T: Default, -{ - pub state: OverlayState, - pub backend: Box, - pub primary_pointer: Option, - pub data: T, -} - -impl Default for OverlayData -where - T: Default, -{ - fn default() -> Self { - Self { - state: OverlayState::default(), - backend: Box::::default(), - primary_pointer: None, - data: Default::default(), - } - } -} - -impl OverlayState { - fn get_transform(&self) -> Affine3A { - self.saved_transform.unwrap_or_else(|| { - Affine3A::from_scale_rotation_translation( - Vec3::ONE * self.spawn_scale, - self.spawn_rotation, - self.spawn_point.into(), - ) - }) - } - - pub fn auto_movement(&mut self, app: &mut AppState) { - let (target_transform, lerp) = match self.positioning { - Positioning::FollowHead { lerp } => (app.input_state.hmd * self.get_transform(), lerp), - Positioning::FollowHand { hand, lerp } => ( - app.input_state.pointers[hand].pose * self.get_transform(), - lerp, - ), - _ => return, - }; - - self.transform = match lerp { - 1.0 => target_transform, - lerp => { - let scale = target_transform.matrix3.x_axis.length(); - - let rot_from = Quat::from_mat3a(&self.transform.matrix3.div_scalar(scale)); - let rot_to = Quat::from_mat3a(&target_transform.matrix3.div_scalar(scale)); - - let rotation = rot_from.slerp(rot_to, lerp); - let translation = self - .transform - .translation - .slerp(target_transform.translation, lerp); - - Affine3A::from_scale_rotation_translation( - Vec3::ONE * scale, - rotation, - translation.into(), - ) - } - }; - - self.dirty = true; - } - - pub fn reset(&mut self, app: &mut AppState, hard_reset: bool) { - let parent_transform = match self.positioning { - Positioning::Floating - | Positioning::FollowHead { .. } - | Positioning::FollowHeadPaused { .. } => app.input_state.hmd, - Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => { - app.input_state.pointers[hand].pose - } - Positioning::Anchored => app.anchor, - Positioning::Static => return, - }; - - if hard_reset { - self.saved_transform = None; - } - - self.transform = parent_transform * self.get_transform(); - - if self.grabbable && hard_reset { - self.realign(&app.input_state.hmd); - } - self.dirty = true; - } - - pub fn save_transform(&mut self, app: &mut AppState) -> bool { - let parent_transform = match self.positioning { - Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y), - Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => { - app.input_state.hmd - } - Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => { - app.input_state.pointers[hand].pose - } - Positioning::Anchored => snap_upright(app.anchor, Vec3A::Y), - Positioning::Static => return false, - }; - - self.saved_transform = Some(parent_transform.inverse() * self.transform); - - true - } - - pub fn realign(&mut self, hmd: &Affine3A) { - let to_hmd = hmd.translation - self.transform.translation; - let up_dir: Vec3A; - - if hmd.x_axis.dot(Vec3A::Y).abs() > 0.2 { - // Snap upright - up_dir = hmd.y_axis; - } else { - let dot = to_hmd.normalize().dot(hmd.z_axis); - let z_dist = to_hmd.length(); - let y_dist = (self.transform.translation.y - hmd.translation.y).abs(); - let x_angle = (y_dist / z_dist).asin(); - - if dot < -f32::EPSILON { - // facing down - let up_point = hmd.translation + z_dist / x_angle.cos() * Vec3A::Y; - up_dir = (up_point - self.transform.translation).normalize(); - } else if dot > f32::EPSILON { - // facing up - let dn_point = hmd.translation + z_dist / x_angle.cos() * Vec3A::NEG_Y; - up_dir = (self.transform.translation - dn_point).normalize(); - } else { - // perfectly upright - up_dir = Vec3A::Y; - } - } - - let scale = self.transform.x_axis.length(); - - let col_z = (self.transform.translation - hmd.translation).normalize(); - let col_y = up_dir; - let col_x = col_y.cross(col_z); - let col_y = col_z.cross(col_x).normalize(); - let col_x = col_x.normalize(); - - let rot = Mat3A::from_quat(self.spawn_rotation) - * Mat3A::from_quat(Quat::from_axis_angle(Vec3::Y, PI)); - self.transform.matrix3 = Mat3A::from_cols(col_x, col_y, col_z).mul_scalar(scale) * rot; - } -} - -impl OverlayData -where - T: Default, -{ - pub fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> { - if self.state.curvature.is_none() { - self.state.curvature = app - .session - .config - .curve_values - .arc_get(self.state.name.as_ref()) - .copied(); - } - - if matches!( - self.state.positioning, - Positioning::Floating | Positioning::Anchored - ) { - let hard_reset; - if let Some(transform) = app - .session - .config - .transform_values - .arc_get(self.state.name.as_ref()) - { - self.state.saved_transform = Some(*transform); - hard_reset = false; - } else { - hard_reset = true; - } - self.state.reset(app, hard_reset); - } - self.backend.init(app) - } - pub fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { - self.backend.should_render(app) - } - pub fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result { - self.backend.render(app, tgt, buf, alpha) - } - pub fn frame_meta(&mut self) -> Option { - self.backend.frame_meta() - } -} - -#[derive(Default, Clone, Copy)] -pub struct FrameMeta { - pub extent: [u32; 3], - pub transform: Affine3A, - pub format: Format, -} - -pub enum ShouldRender { - Should, - Can, - Unable, -} - -pub trait OverlayRenderer { - /// Called once, before the first frame is rendered - fn init(&mut self, app: &mut AppState) -> anyhow::Result<()>; - fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()>; - fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()>; - - /// Called when the presentation layer is ready to present a new frame - fn should_render(&mut self, app: &mut AppState) -> anyhow::Result; - - /// Called when the contents need to be rendered to the swapchain - fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result; - - /// Called to retrieve the effective extent of the image - /// Used for creating swapchains. - /// - /// Must be true if should_render was also true on the same frame. - fn frame_meta(&mut self) -> Option; -} - -pub struct FallbackRenderer; - -impl OverlayRenderer for FallbackRenderer { - fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result { - Ok(ShouldRender::Unable) - } - fn render( - &mut self, - _app: &mut AppState, - _tgt: Arc, - _buf: &mut CommandBuffers, - _alpha: f32, - ) -> anyhow::Result { - Ok(false) - } - fn frame_meta(&mut self) -> Option { - None - } -} -// Boilerplate and dummies - -#[derive(Clone, Copy, Debug, Default)] -pub enum Positioning { - /// Stays in place unless recentered, recenters relative to HMD - #[default] - Floating, - /// Stays in place unless recentered, recenters relative to anchor - Anchored, - /// Following HMD - FollowHead { lerp: f32 }, - /// Normally follows HMD, but paused due to interaction - FollowHeadPaused { lerp: f32 }, - /// Following hand - FollowHand { hand: usize, lerp: f32 }, - /// Normally follows hand, but paused due to interaction - FollowHandPaused { hand: usize, lerp: f32 }, - /// Stays in place, no recentering - Static, -} - -pub struct SplitOverlayBackend { - pub renderer: Box, - pub interaction: Box, -} - -impl Default for SplitOverlayBackend { - fn default() -> Self { - Self { - renderer: Box::new(FallbackRenderer), - interaction: Box::new(DummyInteractionHandler), - } - } -} - -impl OverlayBackend for SplitOverlayBackend { - fn set_renderer(&mut self, renderer: Box) { - self.renderer = renderer; - } - fn set_interaction(&mut self, interaction: Box) { - self.interaction = interaction; - } -} -impl OverlayRenderer for SplitOverlayBackend { - fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> { - self.renderer.init(app) - } - fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> { - self.renderer.pause(app) - } - fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> { - self.renderer.resume(app) - } - fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { - self.renderer.should_render(app) - } - fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result { - self.renderer.render(app, tgt, buf, alpha) - } - fn frame_meta(&mut self) -> Option { - self.renderer.frame_meta() - } -} - -impl InteractionHandler for SplitOverlayBackend { - fn on_left(&mut self, app: &mut AppState, pointer: usize) { - self.interaction.on_left(app, pointer); - } - fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option { - self.interaction.on_hover(app, hit) - } - fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) { - self.interaction.on_scroll(app, hit, delta_y, delta_x); - } - fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { - self.interaction.on_pointer(app, hit, pressed); - } -} - -pub fn ui_transform(extent: [u32; 2]) -> Affine2 { - let aspect = extent[0] as f32 / extent[1] as f32; - let scale = if aspect < 1.0 { - Vec2 { - x: 1.0 / aspect, - y: -1.0, - } - } else { - Vec2 { - x: 1.0, - y: -1.0 * aspect, - } - }; - let center = Vec2 { x: 0.5, y: 0.5 }; - Affine2::from_scale_angle_translation(scale, 0.0, center) -} diff --git a/src/backend/uidev/mod.rs b/src/backend/uidev/mod.rs deleted file mode 100644 index 5b62804..0000000 --- a/src/backend/uidev/mod.rs +++ /dev/null @@ -1,289 +0,0 @@ -use std::sync::Arc; - -use vulkano::{ - image::{view::ImageView, ImageUsage}, - swapchain::{ - acquire_next_image, Surface, SurfaceInfo, Swapchain, SwapchainCreateInfo, - SwapchainPresentInfo, - }, - sync::GpuFuture, - Validated, VulkanError, -}; -use winit::{ - dpi::LogicalSize, - event::{Event, WindowEvent}, - event_loop::ControlFlow, - window::Window, -}; - -use crate::{ - config::load_custom_ui, - config_io, - graphics::{CommandBuffers, WlxGraphics}, - gui::{ - canvas::Canvas, - modular::{modular_canvas, ModularData}, - }, - hid::USE_UINPUT, - state::{AppState, ScreenMeta}, -}; - -use super::{ - input::{TrackedDevice, TrackedDeviceRole}, - overlay::{OverlayID, OverlayRenderer}, -}; - -static LAST_SIZE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); - -struct PreviewState { - canvas: Canvas<(), ModularData>, - swapchain: Arc, - images: Vec>, -} - -impl PreviewState { - fn new( - state: &mut AppState, - surface: Arc, - window: Arc, - panel_name: &str, - ) -> anyhow::Result { - let config = load_custom_ui(panel_name)?; - - let last_size = { - let size_u64 = LAST_SIZE.load(std::sync::atomic::Ordering::Relaxed); - [size_u64 as u32, (size_u64 >> 32) as u32] - }; - - if last_size != config.size { - let logical_size = LogicalSize::new(config.size[0], config.size[1]); - let _ = window.request_inner_size(logical_size); - window.set_min_inner_size(Some(logical_size)); - window.set_max_inner_size(Some(logical_size)); - LAST_SIZE.store( - ((config.size[1] as u64) << 32) | config.size[0] as u64, - std::sync::atomic::Ordering::Relaxed, - ); - } - - let inner_size = window.inner_size(); - let swapchain_size = [inner_size.width, inner_size.height]; - let (swapchain, images) = create_swapchain(&state.graphics, surface, swapchain_size)?; - - let mut canvas = modular_canvas(config.size, &config.elements, state)?; - canvas.init(state)?; - - Ok(Self { - canvas, - swapchain, - images, - }) - } -} - -#[allow(clippy::too_many_lines)] -pub fn uidev_run(panel_name: &str) -> anyhow::Result<()> { - let (graphics, event_loop, window, surface) = WlxGraphics::new_window()?; - window.set_resizable(false); - window.set_title("WlxOverlay UI Preview"); - - USE_UINPUT.store(false, std::sync::atomic::Ordering::Relaxed); - - let mut state = AppState::from_graphics(graphics.clone())?; - add_dummy_devices(&mut state); - add_dummy_screens(&mut state); - - let mut preview = Some(PreviewState::new( - &mut state, - surface.clone(), - window.clone(), - panel_name, - )?); - - let watch_path = config_io::get_config_root().join(format!("{panel_name}.yaml")); - let mut path_last_modified = watch_path.metadata()?.modified()?; - let mut recreate = false; - let mut last_draw = std::time::Instant::now(); - - #[allow(deprecated)] - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); - } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate = true; - } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let new_modified = watch_path.metadata().unwrap().modified().unwrap(); - if new_modified > path_last_modified { - recreate = true; - path_last_modified = new_modified; - } - - if recreate { - drop(preview.take()); - preview = Some( - PreviewState::new(&mut state, surface.clone(), window.clone(), panel_name) - .unwrap(), - ); - recreate = false; - window.request_redraw(); - } - - { - let preview = preview.as_mut().unwrap(); - let (image_index, _, acquire_future) = - match acquire_next_image(preview.swapchain.clone(), None) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - recreate = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - let mut canvas_cmd_buf = CommandBuffers::default(); - let tgt = preview.images[image_index as usize].clone(); - - if let Err(e) = preview - .canvas - .render(&mut state, tgt, &mut canvas_cmd_buf, 1.0) - { - log::error!("failed to render canvas: {e}"); - window.request_redraw(); - } - - last_draw = std::time::Instant::now(); - - canvas_cmd_buf - .execute_after( - state.graphics.graphics_queue.clone(), - Box::new(acquire_future), - ) - .unwrap() - .then_swapchain_present( - graphics.graphics_queue.clone(), - SwapchainPresentInfo::swapchain_image_index( - preview.swapchain.clone(), - image_index, - ), - ) - .then_signal_fence_and_flush() - .unwrap() - .wait(None) - .unwrap(); - } - } - Event::AboutToWait => { - if last_draw.elapsed().as_millis() > 100 { - window.request_redraw(); - } - } - _ => (), - } - })?; - - Ok(()) -} - -fn create_swapchain( - graphics: &WlxGraphics, - surface: Arc, - extent: [u32; 2], -) -> anyhow::Result<(Arc, Vec>)> { - let surface_capabilities = graphics - .device - .physical_device() - .surface_capabilities(&surface, SurfaceInfo::default()) - .unwrap(); // want panic - - let (swapchain, images) = Swapchain::new( - graphics.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format: graphics.native_format, - image_extent: extent, - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), // want panic - ..Default::default() - }, - )?; - - let image_views = images - .into_iter() - // want panic - .map(|image| ImageView::new_default(image).unwrap()) - .collect::>(); - - Ok((swapchain, image_views)) -} - -fn add_dummy_devices(app: &mut AppState) { - app.input_state.devices.push(TrackedDevice { - role: TrackedDeviceRole::Hmd, - soc: Some(0.42), - charging: true, - }); - app.input_state.devices.push(TrackedDevice { - role: TrackedDeviceRole::LeftHand, - soc: Some(0.72), - charging: false, - }); - app.input_state.devices.push(TrackedDevice { - role: TrackedDeviceRole::RightHand, - soc: Some(0.73), - charging: false, - }); - app.input_state.devices.push(TrackedDevice { - role: TrackedDeviceRole::Tracker, - soc: Some(0.65), - charging: false, - }); - app.input_state.devices.push(TrackedDevice { - role: TrackedDeviceRole::Tracker, - soc: Some(0.67), - charging: false, - }); - app.input_state.devices.push(TrackedDevice { - role: TrackedDeviceRole::Tracker, - soc: Some(0.69), - charging: false, - }); -} - -fn add_dummy_screens(app: &mut AppState) { - app.screens.push(ScreenMeta { - name: "HDMI-A-1".into(), - id: OverlayID(0), - native_handle: 0, - }); - app.screens.push(ScreenMeta { - name: "DP-2".into(), - id: OverlayID(0), - native_handle: 0, - }); - app.screens.push(ScreenMeta { - name: "DP-3".into(), - id: OverlayID(0), - native_handle: 0, - }); -} diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 8a94a2d..0000000 --- a/src/config.rs +++ /dev/null @@ -1,521 +0,0 @@ -use std::path::PathBuf; -use std::sync::Arc; - -use crate::config_io; -use crate::gui::modular::ModularUiConfig; -use crate::overlays::toast::DisplayMethod; -use crate::overlays::toast::ToastTopic; -use crate::state::LeftRight; -use anyhow::bail; -use chrono::Offset; -use config::Config; -use config::File; -use glam::vec3a; -use glam::Affine3A; -use glam::Quat; -use glam::Vec3A; -use idmap::IdMap; -use log::error; -use serde::Deserialize; -use serde::Serialize; - -pub type AStrMap = Vec<(Arc, V)>; - -pub trait AStrMapExt { - fn arc_set(&mut self, key: Arc, value: V) -> bool; - fn arc_get(&self, key: &str) -> Option<&V>; - fn arc_rm(&mut self, key: &str) -> Option; -} - -impl AStrMapExt for AStrMap { - fn arc_set(&mut self, key: Arc, value: V) -> bool { - let index = self.iter().position(|(k, _)| k.as_ref().eq(key.as_ref())); - index.map(|i| self.remove(i).1); - self.push((key, value)); - true - } - - fn arc_get(&self, key: &str) -> Option<&V> { - self.iter() - .find_map(|(k, v)| if k.as_ref().eq(key) { Some(v) } else { None }) - } - - fn arc_rm(&mut self, key: &str) -> Option { - let index = self.iter().position(|(k, _)| k.as_ref().eq(key)); - index.map(|i| self.remove(i).1) - } -} - -pub type AStrSet = Vec>; - -pub trait AStrSetExt { - fn arc_set(&mut self, value: Arc) -> bool; - fn arc_get(&self, value: &str) -> bool; - fn arc_rm(&mut self, value: &str) -> bool; -} - -impl AStrSetExt for AStrSet { - fn arc_set(&mut self, value: Arc) -> bool { - if self.iter().any(|v| v.as_ref().eq(value.as_ref())) { - return false; - } - self.push(value); - true - } - - fn arc_get(&self, value: &str) -> bool { - self.iter().any(|v| v.as_ref().eq(value)) - } - - fn arc_rm(&mut self, value: &str) -> bool { - let index = self.iter().position(|v| v.as_ref().eq(value)); - index.is_some_and(|i| { - self.remove(i); - true - }) - } -} - -pub type PwTokenMap = AStrMap; - -pub const fn def_watch_pos() -> Vec3A { - vec3a(-0.03, -0.01, 0.125) -} - -pub const fn def_watch_rot() -> Quat { - Quat::from_xyzw(-0.707_106_6, 0.000_796_361_8, 0.707_106_6, 0.0) -} - -pub const fn def_left() -> LeftRight { - LeftRight::Left -} - -pub const fn def_pw_tokens() -> PwTokenMap { - AStrMap::new() -} - -const fn def_mouse_move_interval_ms() -> u32 { - 10 // 100fps -} - -const fn def_click_freeze_time_ms() -> u32 { - 300 -} - -pub const fn def_true() -> bool { - true -} - -const fn def_false() -> bool { - false -} - -const fn def_one() -> f32 { - 1.0 -} - -pub const fn def_half() -> f32 { - 0.5 -} - -pub const fn def_point7() -> f32 { - 0.7 -} - -pub const fn def_point3() -> f32 { - 0.3 -} - -const fn def_osc_port() -> u16 { - 9000 -} - -const fn def_empty_vec_string() -> Vec { - Vec::new() -} - -fn def_timezones() -> Vec { - const EMEA: i32 = -60 * 60; // UTC-1 - const APAC: i32 = 5 * 60 * 60; // UTC+5 - - let offset = chrono::Local::now().offset().fix(); - match offset.local_minus_utc() { - i32::MIN..EMEA => vec!["Europe/Paris".into(), "Asia/Tokyo".into()], - EMEA..APAC => vec!["America/New_York".into(), "Asia/Tokyo".into()], - APAC..=i32::MAX => vec!["Europe/Paris".into(), "America/New_York".into()], - } -} - -const fn def_screens() -> AStrSet { - AStrSet::new() -} - -const fn def_curve_values() -> AStrMap { - AStrMap::new() -} - -const fn def_transforms() -> AStrMap { - AStrMap::new() -} - -fn def_auto() -> Arc { - "auto".into() -} - -fn def_empty() -> Arc { - "".into() -} - -fn def_toast_topics() -> IdMap { - IdMap::new() -} - -fn def_font() -> Arc { - "LiberationSans:style=Bold".into() -} - -const fn def_max_height() -> u16 { - 1440 -} - -#[derive(Deserialize, Serialize)] -pub struct GeneralConfig { - #[serde(default = "def_watch_pos")] - pub watch_pos: Vec3A, - - #[serde(default = "def_watch_rot")] - pub watch_rot: Quat, - - #[serde(default = "def_left")] - pub watch_hand: LeftRight, - - #[serde(default = "def_click_freeze_time_ms")] - pub click_freeze_time_ms: u32, - - #[serde(default = "def_mouse_move_interval_ms")] - pub mouse_move_interval_ms: u32, - - #[serde(default = "def_true")] - pub notifications_enabled: bool, - - #[serde(default = "def_true")] - pub notifications_sound_enabled: bool, - - #[serde(default = "def_toast_topics")] - pub notification_topics: IdMap, - - #[serde(default = "def_empty")] - pub notification_sound: Arc, - - #[serde(default = "def_true")] - pub keyboard_sound_enabled: bool, - - #[serde(default = "def_one")] - pub keyboard_scale: f32, - - #[serde(default = "def_one")] - pub desktop_view_scale: f32, - - #[serde(default = "def_half")] - pub watch_view_angle_min: f32, - - #[serde(default = "def_point7")] - pub watch_view_angle_max: f32, - - #[serde(default = "def_one")] - pub long_press_duration: f32, - - #[serde(default = "def_osc_port")] - pub osc_out_port: u16, - - #[serde(default = "def_false")] - pub upright_screen_fix: bool, - - #[serde(default = "def_false")] - pub double_cursor_fix: bool, - - #[serde(default = "def_screens")] - pub show_screens: AStrSet, - - #[serde(default = "def_curve_values")] - pub curve_values: AStrMap, - - #[serde(default = "def_transforms")] - pub transform_values: AStrMap, - - #[serde(default = "def_auto")] - pub capture_method: Arc, - - #[serde(default = "def_point7")] - pub xr_grab_sensitivity: f32, - - #[serde(default = "def_point7")] - pub xr_click_sensitivity: f32, - - #[serde(default = "def_point7")] - pub xr_alt_click_sensitivity: f32, - - #[serde(default = "def_half")] - pub xr_grab_sensitivity_release: f32, - - #[serde(default = "def_half")] - pub xr_click_sensitivity_release: f32, - - #[serde(default = "def_half")] - pub xr_alt_click_sensitivity_release: f32, - - #[serde(default = "def_true")] - pub allow_sliding: bool, - - #[serde(default = "def_true")] - pub realign_on_showhide: bool, - - #[serde(default = "def_false")] - pub focus_follows_mouse_mode: bool, - - #[serde(default = "def_false")] - pub left_handed_mouse: bool, - - #[serde(default = "def_false")] - pub block_game_input: bool, - - #[serde(default = "def_true")] - pub block_game_input_ignore_watch: bool, - - #[serde(default = "def_font")] - pub primary_font: Arc, - - #[serde(default = "def_one")] - pub space_drag_multiplier: f32, - - #[serde(default = "def_empty")] - pub skybox_texture: Arc, - - #[serde(default = "def_true")] - pub use_skybox: bool, - - #[serde(default = "def_true")] - pub use_passthrough: bool, - - #[serde(default = "def_max_height")] - pub screen_max_height: u16, - - #[serde(default = "def_false")] - pub screen_render_down: bool, - - #[serde(default = "def_point3")] - pub pointer_lerp_factor: f32, - - #[serde(default = "def_false")] - pub space_rotate_unlocked: bool, - - #[serde(default = "def_empty_vec_string")] - pub alt_click_down: Vec, - - #[serde(default = "def_empty_vec_string")] - pub alt_click_up: Vec, - - #[serde(default = "def_timezones")] - pub timezones: Vec, -} - -impl GeneralConfig { - fn sanitize_range(name: &str, val: f32, from: f32, to: f32) { - assert!( - !(!val.is_normal() || val < from || val > to), - "GeneralConfig: {name} needs to be between {from} and {to}" - ); - } - - pub fn load_from_disk() -> Self { - let config = load_general(); - config.post_load(); - config - } - - fn post_load(&self) { - Self::sanitize_range("keyboard_scale", self.keyboard_scale, 0.05, 5.0); - Self::sanitize_range("desktop_view_scale", self.desktop_view_scale, 0.05, 5.0); - } -} - -const FALLBACKS: [&str; 5] = [ - include_str!("res/keyboard.yaml"), - include_str!("res/watch.yaml"), - include_str!("res/settings.yaml"), - include_str!("res/anchor.yaml"), - include_str!("res/wayvr.yaml"), -]; - -const FILES: [&str; 5] = [ - "keyboard.yaml", - "watch.yaml", - "settings.yaml", - "anchor.yaml", - "wayvr.yaml", -]; - -#[derive(Clone, Copy)] -#[repr(usize)] -pub enum ConfigType { - Keyboard, - Watch, - Settings, - Anchor, - #[allow(dead_code)] - WayVR, -} - -pub fn load_known_yaml(config_type: ConfigType) -> T -where - T: for<'de> Deserialize<'de>, -{ - let fallback = FALLBACKS[config_type as usize]; - let file_name = FILES[config_type as usize]; - let maybe_override = config_io::load(file_name); - - for yaml in [maybe_override.as_deref(), Some(fallback)].iter().flatten() { - match serde_yaml::from_str::(yaml) { - Ok(d) => return d, - Err(e) => { - error!("Failed to parse {file_name}, falling back to defaults."); - error!("{e}"); - } - } - } - // can only get here if internal fallback is broken - panic!("No usable config found."); -} - -pub fn load_custom_ui(name: &str) -> anyhow::Result { - let filename = format!("{name}.yaml"); - let Some(yaml_data) = config_io::load(&filename) else { - bail!("Could not read file at {}", &filename); - }; - Ok(serde_yaml::from_str(&yaml_data)?) -} - -pub fn load_config_with_conf_d( - root_config_filename: &str, - ctype: config_io::ConfigRoot, -) -> ConfigData -where - ConfigData: for<'de> Deserialize<'de>, -{ - let mut settings_builder = Config::builder(); - - // Add files from conf.d directory - let path_conf_d = ctype.get_conf_d_path(); - - for mut base_conf in [config_io::get_config_root(), path_conf_d.clone()] { - base_conf.push(root_config_filename); - if base_conf.exists() { - log::info!("Loading config file: {}", base_conf.to_string_lossy()); - settings_builder = settings_builder.add_source(File::from(base_conf)); - } - } - - if let Ok(paths_unsorted) = std::fs::read_dir(path_conf_d) { - let mut paths: Vec<_> = paths_unsorted - .filter_map(|r| match r { - Ok(entry) => Some(entry), - Err(e) => { - error!("Failed to read conf.d directory: {e}"); - None - } - }) - .collect(); - // Sort paths alphabetically - paths.sort_by_key(std::fs::DirEntry::path); - for path in paths { - log::info!("Loading config file: {}", path.path().to_string_lossy()); - settings_builder = settings_builder.add_source(File::from(path.path())); - } - } - - match settings_builder.build() { - Ok(settings) => match settings.try_deserialize::() { - Ok(config) => config, - Err(e) => { - panic!("Failed to deserialize settings: {e}"); - } - }, - Err(e) => { - panic!("Failed to build settings: {e}"); - } - } -} - -pub fn load_general() -> GeneralConfig { - load_config_with_conf_d::("config.yaml", config_io::ConfigRoot::Generic) -} - -// Config that is saved from the settings panel - -#[derive(Serialize)] -pub struct AutoSettings { - pub watch_pos: Vec3A, - pub watch_rot: Quat, - pub watch_hand: LeftRight, - pub watch_view_angle_min: f32, - pub watch_view_angle_max: f32, - pub notifications_enabled: bool, - pub notifications_sound_enabled: bool, - pub realign_on_showhide: bool, - pub allow_sliding: bool, - pub space_drag_multiplier: f32, -} - -fn get_settings_path() -> PathBuf { - config_io::ConfigRoot::Generic - .get_conf_d_path() - .join("zz-saved-config.json5") -} - -pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> { - let conf = AutoSettings { - watch_pos: config.watch_pos, - watch_rot: config.watch_rot, - watch_hand: config.watch_hand, - watch_view_angle_min: config.watch_view_angle_min, - watch_view_angle_max: config.watch_view_angle_max, - notifications_enabled: config.notifications_enabled, - notifications_sound_enabled: config.notifications_sound_enabled, - realign_on_showhide: config.realign_on_showhide, - allow_sliding: config.allow_sliding, - space_drag_multiplier: config.space_drag_multiplier, - }; - - let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic - std::fs::write(get_settings_path(), json)?; - - Ok(()) -} - -// Config that is saved after manipulating overlays - -#[derive(Serialize)] -pub struct AutoState { - pub show_screens: AStrSet, - pub curve_values: AStrMap, - pub transform_values: AStrMap, -} - -fn get_state_path() -> PathBuf { - config_io::ConfigRoot::Generic - .get_conf_d_path() - .join("zz-saved-state.json5") -} - -pub fn save_layout(config: &GeneralConfig) -> anyhow::Result<()> { - let conf = AutoState { - show_screens: config.show_screens.clone(), - curve_values: config.curve_values.clone(), - transform_values: config.transform_values.clone(), - }; - - let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic - std::fs::write(get_state_path(), json)?; - - Ok(()) -} diff --git a/src/graphics/dmabuf.rs b/src/graphics/dmabuf.rs deleted file mode 100644 index 52df1ee..0000000 --- a/src/graphics/dmabuf.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::{mem::MaybeUninit, sync::Arc}; - -use smallvec::SmallVec; -use vulkano::{ - device::Device, - image::{sys::RawImage, ImageCreateInfo, SubresourceLayout}, - sync::Sharing, - VulkanError, VulkanObject, -}; - -#[allow(clippy::all, clippy::pedantic)] -pub(super) unsafe fn create_dmabuf_image( - device: Arc, - create_info: ImageCreateInfo, -) -> Result { - let &ImageCreateInfo { - flags, - image_type, - format, - ref view_formats, - extent, - array_layers, - mip_levels, - samples, - tiling, - usage, - stencil_usage, - ref sharing, - initial_layout, - ref drm_format_modifiers, - ref drm_format_modifier_plane_layouts, - external_memory_handle_types, - _ne: _, - } = &create_info; - - let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing { - Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, &[] as _), - Sharing::Concurrent(queue_family_indices) => ( - ash::vk::SharingMode::CONCURRENT, - queue_family_indices.len() as u32, - queue_family_indices.as_ptr(), - ), - }; - - let mut create_info_vk = ash::vk::ImageCreateInfo { - flags: flags.into(), - image_type: image_type.into(), - format: format.into(), - extent: ash::vk::Extent3D { - width: extent[0], - height: extent[1], - depth: extent[2], - }, - mip_levels, - array_layers, - samples: samples.into(), - tiling: tiling.into(), - usage: usage.into(), - sharing_mode, - queue_family_index_count, - p_queue_family_indices, - initial_layout: initial_layout.into(), - ..Default::default() - }; - - let mut drm_format_modifier_explicit_info_vk = None; - let drm_format_modifier_plane_layouts_vk: SmallVec<[_; 4]>; - let mut drm_format_modifier_list_info_vk = None; - let mut external_memory_info_vk = None; - let mut format_list_info_vk = None; - let format_list_view_formats_vk: Vec<_>; - let mut stencil_usage_info_vk = None; - - if drm_format_modifiers.len() == 1 { - drm_format_modifier_plane_layouts_vk = drm_format_modifier_plane_layouts - .iter() - .map(|subresource_layout| { - let &SubresourceLayout { - offset, - size, - row_pitch, - array_pitch, - depth_pitch, - } = subresource_layout; - - ash::vk::SubresourceLayout { - offset, - size, - row_pitch, - array_pitch: array_pitch.unwrap_or(0), - depth_pitch: depth_pitch.unwrap_or(0), - } - }) - .collect(); - - let next = drm_format_modifier_explicit_info_vk.insert( - ash::vk::ImageDrmFormatModifierExplicitCreateInfoEXT { - drm_format_modifier: drm_format_modifiers[0], - drm_format_modifier_plane_count: drm_format_modifier_plane_layouts_vk.len() as u32, - p_plane_layouts: drm_format_modifier_plane_layouts_vk.as_ptr(), - ..Default::default() - }, - ); - - next.p_next = create_info_vk.p_next; - create_info_vk.p_next = next as *const _ as *const _; - } else if drm_format_modifiers.len() > 1 { - let next = drm_format_modifier_list_info_vk.insert( - ash::vk::ImageDrmFormatModifierListCreateInfoEXT { - drm_format_modifier_count: drm_format_modifiers.len() as u32, - p_drm_format_modifiers: drm_format_modifiers.as_ptr(), - ..Default::default() - }, - ); - - next.p_next = create_info_vk.p_next; - create_info_vk.p_next = next as *const _ as *const _; - } - - if !external_memory_handle_types.is_empty() { - let next = external_memory_info_vk.insert(ash::vk::ExternalMemoryImageCreateInfo { - handle_types: external_memory_handle_types.into(), - ..Default::default() - }); - - next.p_next = create_info_vk.p_next; - create_info_vk.p_next = next as *const _ as *const _; - } - - if !view_formats.is_empty() { - format_list_view_formats_vk = view_formats - .iter() - .copied() - .map(ash::vk::Format::from) - .collect(); - - let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo { - view_format_count: format_list_view_formats_vk.len() as u32, - p_view_formats: format_list_view_formats_vk.as_ptr(), - ..Default::default() - }); - - next.p_next = create_info_vk.p_next; - create_info_vk.p_next = next as *const _ as *const _; - } - - if let Some(stencil_usage) = stencil_usage { - let next = stencil_usage_info_vk.insert(ash::vk::ImageStencilUsageCreateInfo { - stencil_usage: stencil_usage.into(), - ..Default::default() - }); - - next.p_next = create_info_vk.p_next; - create_info_vk.p_next = next as *const _ as *const _; - } - - let handle = { - let fns = device.fns(); - let mut output = MaybeUninit::uninit(); - (fns.v1_0.create_image)( - device.handle(), - &create_info_vk, - std::ptr::null(), - output.as_mut_ptr(), - ) - .result() - .map_err(VulkanError::from)?; - output.assume_init() - }; - - RawImage::from_handle(device, handle, create_info) -} diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs deleted file mode 100644 index f6c85ea..0000000 --- a/src/graphics/mod.rs +++ /dev/null @@ -1,1579 +0,0 @@ -pub mod dds; -pub mod dmabuf; - -use std::{ - collections::HashMap, - os::fd::{FromRawFd, IntoRawFd}, - slice::Iter, - sync::{Arc, OnceLock, RwLock}, -}; - -use anyhow::{anyhow, bail}; -use ash::vk::SubmitInfo; -use dmabuf::create_dmabuf_image; -use smallvec::smallvec; - -#[cfg(feature = "openvr")] -use vulkano::instance::InstanceCreateFlags; - -#[cfg(feature = "openxr")] -use {ash::vk, std::os::raw::c_void}; - -use vulkano::{ - buffer::{ - allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, - Buffer, BufferContents, BufferCreateInfo, BufferUsage, IndexBuffer, Subbuffer, - }, - command_buffer::{ - allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo}, - AutoCommandBufferBuilder, CommandBufferBeginInfo, CommandBufferExecFuture, - CommandBufferInheritanceInfo, CommandBufferInheritanceRenderPassType, - CommandBufferInheritanceRenderingInfo, CommandBufferLevel, CommandBufferUsage, - CopyBufferToImageInfo, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract, - RecordingCommandBuffer, RenderingAttachmentInfo, RenderingInfo, SecondaryAutoCommandBuffer, - SubpassContents, - }, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, - }, - device::{ - physical::{PhysicalDevice, PhysicalDeviceType}, - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, - QueueFlags, - }, - format::Format, - image::{ - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, - view::ImageView, - Image, ImageCreateInfo, ImageLayout, ImageTiling, ImageType, ImageUsage, SubresourceLayout, - }, - instance::{Instance, InstanceCreateInfo, InstanceExtensions}, - memory::{ - allocator::{ - AllocationCreateInfo, GenericMemoryAllocatorCreateInfo, MemoryAllocator, - MemoryTypeFilter, StandardMemoryAllocator, - }, - DedicatedAllocation, DeviceMemory, ExternalMemoryHandleType, ExternalMemoryHandleTypes, - MemoryAllocateInfo, MemoryImportInfo, MemoryPropertyFlags, ResourceMemory, - }, - pipeline::{ - graphics::{ - color_blend::{ - AttachmentBlend, BlendFactor, BlendOp, ColorBlendAttachmentState, ColorBlendState, - }, - input_assembly::InputAssemblyState, - multisample::MultisampleState, - rasterization::RasterizationState, - subpass::PipelineRenderingCreateInfo, - vertex_input::{Vertex, VertexDefinition}, - viewport::{Viewport, ViewportState}, - GraphicsPipelineCreateInfo, - }, - layout::PipelineDescriptorSetLayoutCreateInfo, - DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, - }, - render_pass::{AttachmentLoadOp, AttachmentStoreOp}, - shader::ShaderModule, - sync::{ - fence::Fence, future::NowFuture, AccessFlags, DependencyInfo, GpuFuture, - ImageMemoryBarrier, PipelineStages, - }, - DeviceSize, VulkanObject, -}; - -use wlx_capture::frame::{ - DmabufFrame, DrmFormat, FourCC, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ABGR8888, - DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR2101010, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, -}; - -pub type Vert2Buf = Subbuffer<[Vert2Uv]>; -pub type IndexBuf = IndexBuffer; - -pub const DRM_FORMAT_MOD_INVALID: u64 = 0xff_ffff_ffff_ffff; - -#[repr(C)] -#[derive(BufferContents, Vertex, Copy, Clone, Debug)] -pub struct Vert2Uv { - #[format(R32G32_SFLOAT)] - pub in_pos: [f32; 2], - #[format(R32G32_SFLOAT)] - pub in_uv: [f32; 2], -} - -pub const INDICES: [u16; 6] = [2, 1, 0, 1, 2, 3]; - -pub const BLEND_ALPHA: AttachmentBlend = AttachmentBlend { - src_color_blend_factor: BlendFactor::SrcAlpha, - dst_color_blend_factor: BlendFactor::OneMinusSrcAlpha, - color_blend_op: BlendOp::Add, - src_alpha_blend_factor: BlendFactor::One, - dst_alpha_blend_factor: BlendFactor::One, - alpha_blend_op: BlendOp::Max, -}; - -pub struct WlxGraphics { - pub instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - pub transfer_queue: Arc, - pub capture_queue: Option>, - - pub native_format: Format, - pub texture_filtering: Filter, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, - - pub quad_verts: Vert2Buf, - pub quad_indices: IndexBuf, - - pub shared_shaders: RwLock>>, - pub drm_formats: Vec, -} - -const fn get_dmabuf_extensions() -> DeviceExtensions { - DeviceExtensions { - khr_external_memory: true, - khr_external_memory_fd: true, - ext_external_memory_dma_buf: true, - ..DeviceExtensions::empty() - } -} - -static VULKAN_LIBRARY: OnceLock> = OnceLock::new(); -fn get_vulkan_library() -> &'static Arc { - VULKAN_LIBRARY.get_or_init(|| vulkano::VulkanLibrary::new().unwrap()) // want panic -} - -#[cfg(feature = "openxr")] -unsafe extern "system" fn get_instance_proc_addr( - instance: openxr::sys::platform::VkInstance, - name: *const std::ffi::c_char, -) -> Option { - use vulkano::Handle; - let instance = ash::vk::Instance::from_raw(instance as _); - let library = get_vulkan_library(); - library.get_instance_proc_addr(instance, name) -} - -impl WlxGraphics { - #[cfg(feature = "openxr")] - #[allow(clippy::too_many_lines)] - pub fn new_openxr( - xr_instance: openxr::Instance, - system: openxr::SystemId, - ) -> anyhow::Result> { - use std::ffi::{self, CString}; - - use vulkano::{ - descriptor_set::allocator::StandardDescriptorSetAllocatorCreateInfo, Handle, Version, - }; - - let instance_extensions = InstanceExtensions { - khr_get_physical_device_properties2: true, - ..InstanceExtensions::empty() - }; - - let instance_extensions_raw = instance_extensions - .into_iter() - .filter_map(|(name, enabled)| { - if enabled { - Some(ffi::CString::new(name).unwrap().into_raw().cast_const()) - // want panic - } else { - None - } - }) - .collect::>(); - - let vk_target_version = vk::make_api_version(0, 1, 3, 0); - let target_version = vulkano::Version::V1_3; - let library = get_vulkan_library(); - - let vk_app_info_raw = vk::ApplicationInfo::default() - .application_version(0) - .engine_version(0) - .api_version(vk_target_version); - - let instance = unsafe { - let vk_instance = xr_instance - .create_vulkan_instance( - system, - get_instance_proc_addr, - std::ptr::from_ref( - &vk::InstanceCreateInfo::default() - .application_info(&vk_app_info_raw) - .enabled_extension_names(&instance_extensions_raw), - ) - .cast(), - ) - .expect("XR error creating Vulkan instance") - .map_err(vk::Result::from_raw) - .expect("Vulkan error creating Vulkan instance"); - - Instance::from_handle( - library.clone(), - ash::vk::Instance::from_raw(vk_instance as _), - InstanceCreateInfo { - application_version: Version::major_minor(0, 0), - engine_version: Version::major_minor(0, 0), - max_api_version: Some(Version::V1_3), - enabled_extensions: instance_extensions, - ..Default::default() - }, - ) - }; - - let physical_device = unsafe { - PhysicalDevice::from_handle( - instance.clone(), - vk::PhysicalDevice::from_raw( - xr_instance.vulkan_graphics_device(system, instance.handle().as_raw() as _)? - as _, - ), - ) - }?; - - let vk_device_properties = physical_device.properties(); - assert!( - (vk_device_properties.api_version >= target_version), - "Vulkan physical device doesn't support Vulkan {target_version}" - ); - - log::info!( - "Using vkPhysicalDevice: {}", - physical_device.properties().device_name, - ); - - let queue_families = try_all_queue_families(physical_device.as_ref()) - .expect("vkPhysicalDevice does not have a GRAPHICS / TRANSFER queue."); - - let mut device_extensions = DeviceExtensions::empty(); - let dmabuf_extensions = get_dmabuf_extensions(); - - if physical_device - .supported_extensions() - .contains(&dmabuf_extensions) - { - device_extensions = device_extensions.union(&dmabuf_extensions); - device_extensions.ext_image_drm_format_modifier = physical_device - .supported_extensions() - .ext_image_drm_format_modifier; - } - - let texture_filtering = if physical_device.supported_extensions().ext_filter_cubic { - device_extensions.ext_filter_cubic = true; - Filter::Cubic - } else { - Filter::Linear - }; - - let device_extensions_raw = device_extensions - .into_iter() - .filter_map(|(name, enabled)| { - if enabled { - Some(ffi::CString::new(name).unwrap().into_raw().cast_const()) - // want panic - } else { - None - } - }) - .collect::>(); - - let features = DeviceFeatures { - dynamic_rendering: true, - ..Default::default() - }; - - let queue_create_infos = queue_families - .iter() - .map(|fam| { - vk::DeviceQueueCreateInfo::default() - .queue_family_index(fam.queue_family_index) - .queue_priorities(&fam.priorities) - }) - .collect::>(); - - let mut device_create_info = vk::DeviceCreateInfo::default() - .queue_create_infos(&queue_create_infos) - .enabled_extension_names(&device_extensions_raw); - - let mut dynamic_rendering = - vk::PhysicalDeviceDynamicRenderingFeatures::default().dynamic_rendering(true); - - dynamic_rendering.p_next = device_create_info.p_next.cast_mut(); - device_create_info.p_next = &raw mut dynamic_rendering as *const c_void; - - let (device, queues) = unsafe { - let vk_device = xr_instance - .create_vulkan_device( - system, - get_instance_proc_addr, - physical_device.handle().as_raw() as _, - (&raw const device_create_info).cast(), - ) - .expect("XR error creating Vulkan device") - .map_err(vk::Result::from_raw) - .expect("Vulkan error creating Vulkan device"); - - vulkano::device::Device::from_handle( - physical_device, - vk::Device::from_raw(vk_device as _), - DeviceCreateInfo { - queue_create_infos: queue_families - .iter() - .map(|fam| QueueCreateInfo { - queue_family_index: fam.queue_family_index, - queues: fam.priorities.clone(), - ..Default::default() - }) - .collect::>(), - enabled_extensions: device_extensions, - enabled_features: features, - ..Default::default() - }, - ) - }; - - log::debug!( - " DMA-buf supported: {}", - device.enabled_extensions().ext_external_memory_dma_buf - ); - log::debug!( - " DRM format modifiers supported: {}", - device.enabled_extensions().ext_image_drm_format_modifier - ); - - // Drop the CStrings - device_extensions_raw - .into_iter() - .for_each(|c_string| unsafe { - let _ = CString::from_raw(c_string.cast_mut()); - }); - - let (graphics_queue, transfer_queue, capture_queue) = unwrap_queues(queues.collect()); - - let memory_allocator = memory_allocator(device.clone()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - StandardDescriptorSetAllocatorCreateInfo::default(), - )); - - let (quad_verts, quad_indices) = Self::default_quad(memory_allocator.clone())?; - let drm_formats = Self::get_drm_formats(device.clone()); - - let me = Self { - instance, - device, - graphics_queue, - transfer_queue, - capture_queue, - native_format: Format::R8G8B8A8_SRGB, - texture_filtering, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - quad_indices, - quad_verts, - shared_shaders: RwLock::new(HashMap::new()), - drm_formats, - }; - - Ok(Arc::new(me)) - } - - #[allow(clippy::too_many_lines)] - #[cfg(feature = "openvr")] - pub fn new_openvr( - mut vk_instance_extensions: InstanceExtensions, - mut vk_device_extensions_fn: impl FnMut(&PhysicalDevice) -> DeviceExtensions, - ) -> anyhow::Result> { - //#[cfg(debug_assertions)] - //let layers = vec!["VK_LAYER_KHRONOS_validation".to_owned()]; - //#[cfg(not(debug_assertions))] - - use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocatorCreateInfo; - - let layers = vec![]; - - log::debug!("Instance exts for runtime: {:?}", &vk_instance_extensions); - - vk_instance_extensions.khr_get_physical_device_properties2 = true; - - let instance = Instance::new( - get_vulkan_library().clone(), - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: vk_instance_extensions, - enabled_layers: layers, - ..Default::default() - }, - )?; - - let dmabuf_extensions = get_dmabuf_extensions(); - - let (physical_device, my_extensions, queue_families) = instance - .enumerate_physical_devices()? - .filter_map(|p| { - let mut my_extensions = vk_device_extensions_fn(&p); - - if !p.supported_extensions().contains(&my_extensions) { - log::debug!( - "Not using {} due to missing extensions:", - p.properties().device_name, - ); - for (ext, missing) in p.supported_extensions().difference(&my_extensions) { - if missing { - log::debug!(" {ext}"); - } - } - return None; - } - - if p.supported_extensions().contains(&dmabuf_extensions) { - my_extensions = my_extensions.union(&dmabuf_extensions); - my_extensions.ext_image_drm_format_modifier = - p.supported_extensions().ext_image_drm_format_modifier; - } - - if p.supported_extensions().ext_filter_cubic { - my_extensions.ext_filter_cubic = true; - } - - log::debug!( - "Device exts for {}: {:?}", - p.properties().device_name, - &my_extensions - ); - Some((p, my_extensions)) - }) - .filter_map(|(p, my_extensions)| { - try_all_queue_families(p.as_ref()).map(|families| (p, my_extensions, families)) - }) - .min_by_key(|(p, _, families)| { - prio_from_device_type(p) * 10 + prio_from_families(families) - }) - .expect("no suitable physical device found"); - - log::info!( - "Using vkPhysicalDevice: {}", - physical_device.properties().device_name, - ); - - let texture_filtering = if physical_device.supported_extensions().ext_filter_cubic { - Filter::Cubic - } else { - Filter::Linear - }; - - let (device, queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: my_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - queue_create_infos: queue_families - .iter() - .map(|fam| QueueCreateInfo { - queue_family_index: fam.queue_family_index, - queues: fam.priorities.clone(), - ..Default::default() - }) - .collect::>(), - ..Default::default() - }, - )?; - - log::debug!( - " DMA-buf supported: {}", - device.enabled_extensions().ext_external_memory_dma_buf - ); - log::debug!( - " DRM format modifiers supported: {}", - device.enabled_extensions().ext_image_drm_format_modifier - ); - - let (graphics_queue, transfer_queue, capture_queue) = unwrap_queues(queues.collect()); - - let memory_allocator = memory_allocator(device.clone()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - StandardDescriptorSetAllocatorCreateInfo::default(), - )); - - let (quad_verts, quad_indices) = Self::default_quad(memory_allocator.clone())?; - let drm_formats = Self::get_drm_formats(device.clone()); - - let me = Self { - instance, - device, - graphics_queue, - transfer_queue, - capture_queue, - memory_allocator, - native_format: Format::R8G8B8A8_SRGB, - texture_filtering, - command_buffer_allocator, - descriptor_set_allocator, - quad_indices, - quad_verts, - shared_shaders: RwLock::new(HashMap::new()), - drm_formats, - }; - - Ok(Arc::new(me)) - } - - #[cfg(feature = "uidev")] - #[allow(clippy::type_complexity, clippy::too_many_lines)] - pub fn new_window() -> anyhow::Result<( - Arc, - winit::event_loop::EventLoop<()>, - Arc, - Arc, - )> { - use vulkano::{ - descriptor_set::allocator::StandardDescriptorSetAllocatorCreateInfo, - instance::InstanceCreateFlags, - swapchain::{Surface, SurfaceInfo}, - }; - use winit::{event_loop::EventLoop, window::Window}; - - let event_loop = EventLoop::new().unwrap(); // want panic - let mut vk_instance_extensions = Surface::required_extensions(&event_loop).unwrap(); - vk_instance_extensions.khr_get_physical_device_properties2 = true; - log::debug!("Instance exts for runtime: {:?}", &vk_instance_extensions); - - let instance = Instance::new( - get_vulkan_library().clone(), - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: vk_instance_extensions, - ..Default::default() - }, - )?; - - #[allow(deprecated)] - let window = Arc::new( - event_loop - .create_window(Window::default_attributes()) - .unwrap(), // want panic - ); - let surface = Surface::from_window(instance.clone(), window.clone())?; - - let mut device_extensions = DeviceExtensions::empty(); - device_extensions.khr_swapchain = true; - - log::debug!("Device exts for app: {:?}", &device_extensions); - - let (physical_device, my_extensions, queue_families) = instance - .enumerate_physical_devices()? - .filter_map(|p| { - if p.supported_extensions().contains(&device_extensions) { - Some((p, device_extensions)) - } else { - log::debug!( - "Not using {} because it does not implement the following device extensions:", - p.properties().device_name, - ); - for (ext, missing) in p.supported_extensions().difference(&device_extensions) { - if missing { - log::debug!(" {ext}"); - } - } - None - } - }) - .filter_map(|(p, my_extensions)| - try_all_queue_families(p.as_ref()).map(|families| (p, my_extensions, families)) - ) - .min_by_key(|(p, _, _)| prio_from_device_type(p) - ) - .expect("no suitable physical device found"); - - log::info!( - "Using vkPhysicalDevice: {}", - physical_device.properties().device_name, - ); - - let (device, queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: my_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - queue_create_infos: queue_families - .iter() - .map(|fam| QueueCreateInfo { - queue_family_index: fam.queue_family_index, - queues: fam.priorities.clone(), - ..Default::default() - }) - .collect::>(), - ..Default::default() - }, - )?; - - let (graphics_queue, transfer_queue, capture_queue) = unwrap_queues(queues.collect()); - - let native_format = device - .physical_device() - .surface_formats(&surface, SurfaceInfo::default()) - .unwrap()[0] // want panic - .0; - log::info!("Using surface format: {native_format:?}"); - - let memory_allocator = memory_allocator(device.clone()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - StandardDescriptorSetAllocatorCreateInfo::default(), - )); - - let (quad_verts, quad_indices) = Self::default_quad(memory_allocator.clone())?; - let drm_formats = Self::get_drm_formats(device.clone()); - - let me = Self { - instance, - device, - graphics_queue, - transfer_queue, - capture_queue, - memory_allocator, - native_format, - texture_filtering: Filter::Linear, - command_buffer_allocator, - descriptor_set_allocator, - quad_indices, - quad_verts, - shared_shaders: RwLock::new(HashMap::new()), - drm_formats, - }; - - Ok((Arc::new(me), event_loop, window, surface)) - } - fn default_quad( - memory_allocator: Arc, - ) -> anyhow::Result<(Vert2Buf, IndexBuf)> { - let vertices = [ - Vert2Uv { - in_pos: [0., 0.], - in_uv: [0., 0.], - }, - Vert2Uv { - in_pos: [0., 1.], - in_uv: [0., 1.], - }, - Vert2Uv { - in_pos: [1., 0.], - in_uv: [1., 0.], - }, - Vert2Uv { - in_pos: [1., 1.], - in_uv: [1., 1.], - }, - ]; - let quad_verts = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices.into_iter(), - )?; - - let quad_indices = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::INDEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - INDICES.iter().copied(), - )?; - - Ok((quad_verts, IndexBuffer::U16(quad_indices))) - } - - pub fn upload_verts( - &self, - width: f32, - height: f32, - x: f32, - y: f32, - w: f32, - h: f32, - ) -> anyhow::Result { - let rw = width; - let rh = height; - - let x0 = x / rw; - let y0 = y / rh; - - let x1 = w / rw + x0; - let y1 = h / rh + y0; - - let vertices = [ - Vert2Uv { - in_pos: [x0, y0], - in_uv: [0.0, 0.0], - }, - Vert2Uv { - in_pos: [x0, y1], - in_uv: [0.0, 1.0], - }, - Vert2Uv { - in_pos: [x1, y0], - in_uv: [1.0, 0.0], - }, - Vert2Uv { - in_pos: [x1, y1], - in_uv: [1.0, 1.0], - }, - ]; - self.upload_buffer(BufferUsage::VERTEX_BUFFER, vertices.iter()) - } - - pub fn upload_buffer( - &self, - usage: BufferUsage, - contents: Iter<'_, T>, - ) -> anyhow::Result> - where - T: BufferContents + Clone, - { - Ok(Buffer::from_iter( - self.memory_allocator.clone(), - BufferCreateInfo { - usage, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - contents.cloned(), - )?) - } - - fn get_drm_formats(device: Arc) -> Vec { - let possible_formats = [ - DRM_FORMAT_ABGR8888.into(), - DRM_FORMAT_XBGR8888.into(), - DRM_FORMAT_ARGB8888.into(), - DRM_FORMAT_XRGB8888.into(), - DRM_FORMAT_ABGR2101010.into(), - DRM_FORMAT_XBGR2101010.into(), - ]; - - let mut final_formats = vec![]; - - for &f in &possible_formats { - let Ok(vk_fmt) = fourcc_to_vk(f) else { - continue; - }; - let Ok(props) = device.physical_device().format_properties(vk_fmt) else { - continue; - }; - let mut fmt = DrmFormat { - fourcc: f, - modifiers: props - .drm_format_modifier_properties - .iter() - // important bit: only allow single-plane - .filter(|m| m.drm_format_modifier_plane_count == 1) - .map(|m| m.drm_format_modifier) - .collect(), - }; - fmt.modifiers.push(DRM_FORMAT_MOD_INVALID); // implicit modifiers support - final_formats.push(fmt); - } - log::debug!("Supported DRM formats:"); - for f in &final_formats { - log::debug!(" {} {:?}", f.fourcc, f.modifiers); - } - final_formats - } - - pub fn dmabuf_texture_ex( - &self, - frame: DmabufFrame, - tiling: ImageTiling, - layouts: Vec, - modifiers: &[u64], - ) -> anyhow::Result> { - let extent = [frame.format.width, frame.format.height, 1]; - let format = fourcc_to_vk(frame.format.fourcc)?; - - let image = unsafe { - create_dmabuf_image( - self.device.clone(), - ImageCreateInfo { - format, - extent, - usage: ImageUsage::SAMPLED, - external_memory_handle_types: ExternalMemoryHandleTypes::DMA_BUF, - tiling, - drm_format_modifiers: modifiers.to_owned(), - drm_format_modifier_plane_layouts: layouts, - ..Default::default() - }, - )? - }; - - let requirements = image.memory_requirements()[0]; - let memory_type_index = self - .memory_allocator - .find_memory_type_index( - requirements.memory_type_bits, - MemoryTypeFilter { - required_flags: MemoryPropertyFlags::DEVICE_LOCAL, - ..Default::default() - }, - ) - .ok_or_else(|| anyhow!("failed to get memory type index"))?; - - debug_assert!(self.device.enabled_extensions().khr_external_memory_fd); - debug_assert!(self.device.enabled_extensions().khr_external_memory); - debug_assert!(self.device.enabled_extensions().ext_external_memory_dma_buf); - - // only do the 1st - unsafe { - let Some(fd) = frame.planes[0].fd else { - bail!("DMA-buf plane has no FD"); - }; - - let file = std::fs::File::from_raw_fd(fd); - let new_file = file.try_clone()?; - let _ = file.into_raw_fd(); - - let memory = DeviceMemory::allocate_unchecked( - self.device.clone(), - MemoryAllocateInfo { - allocation_size: requirements.layout.size(), - memory_type_index, - dedicated_allocation: Some(DedicatedAllocation::Image(&image)), - ..Default::default() - }, - Some(MemoryImportInfo::Fd { - file: new_file, - handle_type: ExternalMemoryHandleType::DmaBuf, - }), - )?; - - let mem_alloc = ResourceMemory::new_dedicated(memory); - match image.bind_memory_unchecked([mem_alloc]) { - Ok(image) => Ok(Arc::new(image)), - Err(e) => { - bail!("Failed to bind memory to image: {}", e.0); - } - } - } - } - - pub fn dmabuf_texture(&self, frame: DmabufFrame) -> anyhow::Result> { - let mut modifiers: Vec = vec![]; - let mut tiling: ImageTiling = ImageTiling::Optimal; - let mut layouts: Vec = vec![]; - - if frame.format.modifier != DRM_FORMAT_MOD_INVALID { - (0..frame.num_planes).for_each(|i| { - let plane = &frame.planes[i]; - layouts.push(SubresourceLayout { - offset: plane.offset.into(), - size: 0, - row_pitch: plane.stride as _, - array_pitch: None, - depth_pitch: None, - }); - modifiers.push(frame.format.modifier); - }); - tiling = ImageTiling::DrmFormatModifier; - } - - self.dmabuf_texture_ex(frame, tiling, layouts, &modifiers) - } - - pub fn render_texture( - &self, - width: u32, - height: u32, - format: Format, - ) -> anyhow::Result> { - log::debug!( - "Render texture: {}x{} {}MB", - width, - height, - (width * height * 4) / (1024 * 1024) - ); - Ok(Image::new( - self.memory_allocator.clone(), - ImageCreateInfo { - image_type: ImageType::Dim2d, - format, - extent: [width, height, 1], - usage: ImageUsage::TRANSFER_SRC - | ImageUsage::SAMPLED - | ImageUsage::COLOR_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - )?) - } - - pub fn create_pipeline( - self: &Arc, - vert: Arc, - frag: Arc, - format: Format, - blend: Option, - ) -> anyhow::Result> { - Ok(Arc::new(WlxPipeline::new( - self.clone(), - vert, - frag, - format, - blend, - )?)) - } - - /// Creates a CommandBuffer to be used for graphics workloads on the main thread. - pub fn create_command_buffer( - self: &Arc, - usage: CommandBufferUsage, - ) -> anyhow::Result { - let command_buffer = AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.graphics_queue.queue_family_index(), - usage, - )?; - Ok(WlxCommandBuffer { - graphics: self.clone(), - queue: self.graphics_queue.clone(), - command_buffer, - dummy: None, - }) - } - - /// Creates a CommandBuffer to be used for texture uploads on the main thread. - pub fn create_uploads_command_buffer( - self: &Arc, - queue: Arc, - usage: CommandBufferUsage, - ) -> anyhow::Result { - let command_buffer = AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - queue.queue_family_index(), - usage, - )?; - Ok(WlxUploadsBuffer { - graphics: self.clone(), - queue, - command_buffer, - dummy: None, - }) - } - - pub fn transition_layout( - &self, - image: Arc, - old_layout: ImageLayout, - new_layout: ImageLayout, - ) -> anyhow::Result { - let barrier = ImageMemoryBarrier { - src_stages: PipelineStages::ALL_TRANSFER, - src_access: AccessFlags::TRANSFER_WRITE, - dst_stages: PipelineStages::ALL_TRANSFER, - dst_access: AccessFlags::TRANSFER_READ, - old_layout, - new_layout, - subresource_range: image.subresource_range(), - ..ImageMemoryBarrier::image(image) - }; - - let command_buffer = unsafe { - let mut builder = RecordingCommandBuffer::new( - self.command_buffer_allocator.clone(), - self.graphics_queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - inheritance_info: None, - ..Default::default() - }, - )?; - - builder.pipeline_barrier(&DependencyInfo { - image_memory_barriers: smallvec![barrier], - ..Default::default() - })?; - builder.end()? - }; - - let fence = vulkano::sync::fence::Fence::new( - self.device.clone(), - vulkano::sync::fence::FenceCreateInfo::default(), - )?; - - let fns = self.device.fns(); - unsafe { - (fns.v1_0.queue_submit)( - self.graphics_queue.handle(), - 1, - [SubmitInfo::default().command_buffers(&[command_buffer.handle()])].as_ptr(), - fence.handle(), - ) - } - .result()?; - - Ok(fence) - } -} - -pub type WlxCommandBuffer = AnyCommandBuffer; -pub type WlxUploadsBuffer = AnyCommandBuffer; - -pub struct GraphicsBuffer; -pub struct UploadBuffer; - -pub struct AnyCommandBuffer { - pub graphics: Arc, - pub command_buffer: AutoCommandBufferBuilder, - pub queue: Arc, - dummy: Option, -} - -impl AnyCommandBuffer { - pub fn build_and_execute(self) -> anyhow::Result> { - let queue = self.queue.clone(); - Ok(self.command_buffer.build()?.execute(queue)?) - } - - pub fn build_and_execute_now(self) -> anyhow::Result<()> { - let mut exec = self.build_and_execute()?; - exec.flush()?; - exec.cleanup_finished(); - Ok(()) - } -} - -impl AnyCommandBuffer { - pub fn begin_rendering(&mut self, render_target: Arc) -> anyhow::Result<()> { - self.command_buffer.begin_rendering(RenderingInfo { - contents: SubpassContents::SecondaryCommandBuffers, - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), - ..RenderingAttachmentInfo::image_view(render_target) - })], - ..Default::default() - })?; - Ok(()) - } - - pub fn build(self) -> anyhow::Result> { - Ok(self.command_buffer.build()?) - } - - pub fn run_ref(&mut self, pass: &WlxPass) -> anyhow::Result<()> { - self.command_buffer - .execute_commands(pass.command_buffer.clone())?; - Ok(()) - } - - pub fn end_rendering(&mut self) -> anyhow::Result<()> { - self.command_buffer.end_rendering()?; - Ok(()) - } -} - -impl AnyCommandBuffer { - pub fn texture2d_raw( - &mut self, - width: u32, - height: u32, - format: Format, - data: &[u8], - ) -> anyhow::Result> { - log::debug!( - "Texture2D: {}x{} {}MB", - width, - height, - data.len() / (1024 * 1024) - ); - let image = Image::new( - self.graphics.memory_allocator.clone(), - ImageCreateInfo { - image_type: ImageType::Dim2d, - format, - extent: [width, height, 1], - usage: ImageUsage::TRANSFER_DST | ImageUsage::TRANSFER_SRC | ImageUsage::SAMPLED, - ..Default::default() - }, - AllocationCreateInfo::default(), - )?; - - let buffer: Subbuffer<[u8]> = Buffer::new_slice( - self.graphics.memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - data.len() as DeviceSize, - )?; - - buffer.write()?.copy_from_slice(data); - - self.command_buffer - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(buffer, image.clone()))?; - - Ok(image) - } -} - -pub struct WlxPipeline { - pub graphics: Arc, - pub pipeline: Arc, - pub format: Format, -} - -impl WlxPipeline { - fn new( - graphics: Arc, - vert: Arc, - frag: Arc, - format: Format, - blend: Option, - ) -> anyhow::Result { - let vep = vert.entry_point("main").unwrap(); // want panic - let fep = frag.entry_point("main").unwrap(); // want panic - - let vertex_input_state = Vert2Uv::per_vertex().definition(&vep)?; - - let stages = smallvec![ - vulkano::pipeline::PipelineShaderStageCreateInfo::new(vep), - vulkano::pipeline::PipelineShaderStageCreateInfo::new(fep), - ]; - - let layout = PipelineLayout::new( - graphics.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(graphics.device.clone())?, - )?; - - let subpass = PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(format)], - ..Default::default() - }; - - let pipeline = GraphicsPipeline::new( - graphics.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages, - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState { - attachments: vec![ColorBlendAttachmentState { - blend, - ..Default::default() - }], - ..Default::default() - }), - dynamic_state: std::iter::once(DynamicState::Viewport).collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - )?; - - Ok(Self { - graphics, - pipeline, - format, - }) - } - pub fn create_pass( - self: &Arc, - dimensions: [f32; 2], - vertex_buffer: Vert2Buf, - index_buffer: IndexBuf, - descriptor_sets: Vec>, - ) -> anyhow::Result { - WlxPass::new( - self.clone(), - dimensions, - vertex_buffer, - index_buffer, - descriptor_sets, - ) - } - - pub fn create_pass_for_target( - self: &Arc, - tgt: Arc, - descriptor_sets: Vec>, - ) -> anyhow::Result { - let extent = tgt.image().extent(); - WlxPass::new( - self.clone(), - [extent[0] as _, extent[1] as _], - self.graphics.quad_verts.clone(), - self.graphics.quad_indices.clone(), - descriptor_sets, - ) - } -} - -impl WlxPipeline { - pub fn inner(&self) -> Arc { - self.pipeline.clone() - } - - pub fn uniform_sampler( - &self, - set: usize, - texture: Arc, - filter: Filter, - ) -> anyhow::Result> { - let sampler = Sampler::new( - self.graphics.device.clone(), - SamplerCreateInfo { - mag_filter: filter, - min_filter: filter, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - )?; - - let layout = self.pipeline.layout().set_layouts().get(set).unwrap(); // want panic - - Ok(DescriptorSet::new( - self.graphics.descriptor_set_allocator.clone(), - layout.clone(), - [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], - [], - )?) - } - - pub fn uniform_buffer(&self, set: usize, data: Vec) -> anyhow::Result> - where - T: BufferContents + Copy, - { - let uniform_buffer = SubbufferAllocator::new( - self.graphics.memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - let uniform_buffer_subbuffer = { - let subbuffer = uniform_buffer.allocate_slice(data.len() as _)?; - subbuffer.write()?.copy_from_slice(data.as_slice()); - subbuffer - }; - - let layout = self.pipeline.layout().set_layouts().get(set).unwrap(); // want panic - Ok(DescriptorSet::new( - self.graphics.descriptor_set_allocator.clone(), - layout.clone(), - [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)], - [], - )?) - } -} - -pub struct WlxPass { - pub command_buffer: Arc, -} - -impl WlxPass { - fn new( - pipeline: Arc, - dimensions: [f32; 2], - vertex_buffer: Vert2Buf, - index_buffer: IndexBuf, - descriptor_sets: Vec>, - ) -> anyhow::Result { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: dimensions, - depth_range: 0.0..=1.0, - }; - let pipeline_inner = pipeline.inner(); - let mut command_buffer = AutoCommandBufferBuilder::secondary( - pipeline.graphics.command_buffer_allocator.clone(), - pipeline.graphics.graphics_queue.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(CommandBufferInheritanceRenderPassType::BeginRendering( - CommandBufferInheritanceRenderingInfo { - color_attachment_formats: vec![Some(pipeline.format)], - - ..Default::default() - }, - )), - ..Default::default() - }, - )?; - - unsafe { - command_buffer - .set_viewport(0, smallvec![viewport])? - .bind_pipeline_graphics(pipeline_inner)? - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - pipeline.inner().layout().clone(), - 0, - descriptor_sets, - )? - .bind_vertex_buffers(0, vertex_buffer)? - .bind_index_buffer(index_buffer.clone())? - .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)? - }; - - Ok(Self { - command_buffer: command_buffer.build()?, - }) - } -} - -#[derive(Default)] -pub struct CommandBuffers { - inner: Vec>, -} - -impl CommandBuffers { - pub fn push(&mut self, buffer: Arc) { - self.inner.push(buffer); - } - pub fn execute_now(self, queue: Arc) -> anyhow::Result>> { - let mut buffers = self.inner.into_iter(); - let Some(first) = buffers.next() else { - return Ok(None); - }; - - let future = first.execute(queue)?; - let mut future: Box = Box::new(future); - - for buf in buffers { - future = Box::new(future.then_execute_same_queue(buf)?); - } - - Ok(Some(future)) - } - #[cfg(feature = "uidev")] - pub fn execute_after( - self, - queue: Arc, - future: Box, - ) -> anyhow::Result> { - let mut buffers = self.inner.into_iter(); - let Some(first) = buffers.next() else { - return Ok(future); - }; - - let future = future.then_execute(queue, first)?; - let mut future: Box = Box::new(future); - - for buf in buffers { - future = Box::new(future.then_execute_same_queue(buf)?); - } - - Ok(future) - } -} - -pub fn fourcc_to_vk(fourcc: FourCC) -> anyhow::Result { - match fourcc.value { - DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(Format::R8G8B8A8_UNORM), - DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(Format::B8G8R8A8_UNORM), - DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => Ok(Format::A2B10G10R10_UNORM_PACK32), - _ => bail!("Unsupported format {}", fourcc), - } -} - -fn memory_allocator(device: Arc) -> Arc { - let props = device.physical_device().memory_properties(); - - let mut block_sizes = vec![0; props.memory_types.len()]; - let mut memory_type_bits = u32::MAX; - - for (index, memory_type) in props.memory_types.iter().enumerate() { - const LARGE_HEAP_THRESHOLD: DeviceSize = 1024 * 1024 * 1024; - - let heap_size = props.memory_heaps[memory_type.heap_index as usize].size; - - block_sizes[index] = if heap_size >= LARGE_HEAP_THRESHOLD { - 48 * 1024 * 1024 - } else { - 24 * 1024 * 1024 - }; - - if memory_type.property_flags.intersects( - MemoryPropertyFlags::LAZILY_ALLOCATED - | MemoryPropertyFlags::PROTECTED - | MemoryPropertyFlags::DEVICE_COHERENT - | MemoryPropertyFlags::RDMA_CAPABLE, - ) { - // VUID-VkMemoryAllocateInfo-memoryTypeIndex-01872 - // VUID-vkAllocateMemory-deviceCoherentMemory-02790 - // Lazily allocated memory would just cause problems for suballocation in general. - memory_type_bits &= !(1 << index); - } - } - - let create_info = GenericMemoryAllocatorCreateInfo { - block_sizes: &block_sizes, - memory_type_bits, - ..Default::default() - }; - - Arc::new(StandardMemoryAllocator::new(device, create_info)) -} - -#[derive(Debug)] -struct QueueFamilyLayout { - queue_family_index: u32, - priorities: Vec, -} - -fn prio_from_device_type(physical_device: &PhysicalDevice) -> u32 { - match physical_device.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - _ => 4, - } -} - -const fn prio_from_families(families: &[QueueFamilyLayout]) -> u32 { - match families.len() { - 2 | 3 => 0, - _ => 1, - } -} - -fn unwrap_queues(queues: Vec>) -> (Arc, Arc, Option>) { - match queues[..] { - [ref g, ref t, ref c] => (g.clone(), t.clone(), Some(c.clone())), - [ref gt, ref c] => (gt.clone(), gt.clone(), Some(c.clone())), - [ref gt] => (gt.clone(), gt.clone(), None), - _ => unreachable!(), - } -} - -fn try_all_queue_families(physical_device: &PhysicalDevice) -> Option> { - queue_families_priorities( - physical_device, - vec![ - // main-thread graphics + uploads - QueueFlags::GRAPHICS | QueueFlags::TRANSFER, - // capture-thread uploads - QueueFlags::TRANSFER, - ], - ) - .or_else(|| { - queue_families_priorities( - physical_device, - vec![ - // main thread graphics - QueueFlags::GRAPHICS, - // main thread uploads - QueueFlags::TRANSFER, - // capture thread uploads - QueueFlags::TRANSFER, - ], - ) - }) - .or_else(|| { - queue_families_priorities( - physical_device, - // main thread-only. software capture not supported. - vec![QueueFlags::GRAPHICS | QueueFlags::TRANSFER], - ) - }) -} - -fn queue_families_priorities( - physical_device: &PhysicalDevice, - mut requested_queues: Vec, -) -> Option> { - let mut result = Vec::with_capacity(3); - - for (idx, props) in physical_device.queue_family_properties().iter().enumerate() { - let mut remaining = props.queue_count; - let mut want = 0usize; - - requested_queues.retain(|requested| { - if props.queue_flags.intersects(*requested) && remaining > 0 { - remaining -= 1; - want += 1; - false - } else { - true - } - }); - - if want > 0 { - result.push(QueueFamilyLayout { - queue_family_index: idx as u32, - priorities: std::iter::repeat_n(1.0, want).collect(), - }); - } - } - - if requested_queues.is_empty() { - log::debug!("Selected GPU queue families: {result:?}"); - Some(result) - } else { - None - } -} diff --git a/src/gui/canvas/builder.rs b/src/gui/canvas/builder.rs deleted file mode 100644 index 40f1838..0000000 --- a/src/gui/canvas/builder.rs +++ /dev/null @@ -1,267 +0,0 @@ -use glam::Vec4; -use std::sync::Arc; - -use vulkano::format::Format; - -use crate::{ - graphics::WlxGraphics, - gui::{canvas::control::ControlRenderer, GuiColor, KeyCapType}, -}; - -use super::{control::Control, Canvas, Rect}; - -pub struct CanvasBuilder { - canvas: Canvas, - - pub fg_color: GuiColor, - pub bg_color: GuiColor, - pub font_size: isize, -} - -impl CanvasBuilder { - pub fn new( - width: usize, - height: usize, - graphics: Arc, - format: Format, - data: D, - ) -> anyhow::Result { - Ok(Self { - canvas: Canvas::new(width, height, graphics, format, data)?, - bg_color: Vec4::ZERO, - fg_color: Vec4::ONE, - font_size: 16, - }) - } - - pub fn build(self) -> Canvas { - self.canvas - } - - // Creates a panel with bg_color inherited from the canvas - pub fn panel(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32) -> &mut Control { - let idx = self.canvas.controls.len(); - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: radius, - bg_color: self.bg_color, - on_render_bg: Some(Control::render_rounded_rect), - ..Control::new() - }); - &mut self.canvas.controls[idx] - } - - // Creates a label with fg_color, font_size inherited from the canvas - pub fn label( - &mut self, - x: f32, - y: f32, - w: f32, - h: f32, - radius: f32, - text: Arc, - ) -> &mut Control { - let idx = self.canvas.controls.len(); - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: radius, - text, - fg_color: self.fg_color, - size: self.font_size, - on_render_fg: Some(Control::render_text), - ..Control::new() - }); - &mut self.canvas.controls[idx] - } - - // Creates a label with fg_color, font_size inherited from the canvas - #[allow(dead_code)] - pub fn label_centered( - &mut self, - x: f32, - y: f32, - w: f32, - h: f32, - radius: f32, - text: Arc, - ) -> &mut Control { - let idx = self.canvas.controls.len(); - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: radius, - text, - fg_color: self.fg_color, - size: self.font_size, - on_render_fg: Some(Control::render_text_centered), - ..Control::new() - }); - &mut self.canvas.controls[idx] - } - - // Creates a sprite. Will not draw anything until set_sprite is called. - pub fn sprite(&mut self, x: f32, y: f32, w: f32, h: f32) -> &mut Control { - let idx = self.canvas.controls.len(); - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: 0., - on_render_bg: Some(Control::render_sprite_bg), - ..Control::new() - }); - &mut self.canvas.controls[idx] - } - - // Creates a sprite that highlights on pointer hover. Will not draw anything until set_sprite is called. - #[allow(dead_code)] - pub fn sprite_interactive(&mut self, x: f32, y: f32, w: f32, h: f32) -> &mut Control { - let idx = self.canvas.controls.len(); - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: 0., - on_render_bg: Some(Control::render_sprite_bg), - on_render_hl: Some(Control::render_sprite_hl), - ..Control::new() - }); - &mut self.canvas.controls[idx] - } - - // Creates a button with fg_color, bg_color, font_size inherited from the canvas - pub fn button( - &mut self, - x: f32, - y: f32, - w: f32, - h: f32, - radius: f32, - text: Arc, - ) -> &mut Control { - let idx = self.canvas.controls.len(); - - self.canvas.interactive_set_idx(x, y, w, h, idx); - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: radius, - text, - fg_color: self.fg_color, - bg_color: self.bg_color, - size: self.font_size, - on_render_bg: Some(Control::render_rounded_rect), - on_render_fg: Some(Control::render_text_centered), - on_render_hl: Some(Control::render_highlight), - ..Control::new() - }); - - &mut self.canvas.controls[idx] - } - - #[allow(clippy::too_many_arguments)] - pub fn key_button( - &mut self, - x: f32, - y: f32, - w: f32, - h: f32, - radius: f32, - cap_type: KeyCapType, - label: &[String], - ) -> &mut Control { - let idx = self.canvas.controls.len(); - self.canvas.interactive_set_idx(x, y, w, h, idx); - - self.canvas.controls.push(Control { - rect: Rect { x, y, w, h }, - corner_radius: radius, - bg_color: self.bg_color, - on_render_bg: Some(Control::render_rounded_rect), - on_render_hl: Some(Control::render_highlight), - ..Control::new() - }); - - let renders = match cap_type { - KeyCapType::Regular => { - let render: ControlRenderer = Control::render_text_centered; - let rect = Rect { - x, - y, - w, - h: h - self.font_size as f32, - }; - vec![(render, rect, 1f32)] - } - KeyCapType::RegularAltGr => { - let render: ControlRenderer = Control::render_text; - let rect0 = Rect { - x: x + 12., - y: y + (self.font_size as f32) + 12., - w, - h, - }; - let rect1 = Rect { - x: w.mul_add(0.5, x) + 12., - y: y + h - (self.font_size as f32) + 8., - w, - h, - }; - vec![(render, rect0, 1.0), (render, rect1, 0.8)] - } - KeyCapType::Reversed => { - let render: ControlRenderer = Control::render_text_centered; - let rect0 = Rect { - x, - y: y + 2.0, - w, - h: h * 0.5, - }; - let rect1 = Rect { - x, - y: h.mul_add(0.5, y) + 2.0, - w, - h: h * 0.5, - }; - vec![(render, rect1, 1.0), (render, rect0, 0.8)] - } - KeyCapType::ReversedAltGr => { - let render: ControlRenderer = Control::render_text; - let rect0 = Rect { - x: x + 12., - y: y + (self.font_size as f32) + 8., - w, - h, - }; - let rect1 = Rect { - x: x + 12., - y: y + h - (self.font_size as f32) + 4., - w, - h, - }; - let rect2 = Rect { - x: w.mul_add(0.5, x) + 8., - y: y + h - (self.font_size as f32) + 4., - w, - h, - }; - vec![ - (render, rect1, 1.0), - (render, rect0, 0.8), - (render, rect2, 0.8), - ] - } - }; - - for (idx, (render, rect, alpha)) in renders.into_iter().enumerate() { - if idx >= label.len() { - break; - } - - self.canvas.controls.push(Control { - rect, - text: Arc::from(label[idx].as_str()), - fg_color: self.fg_color * alpha, - size: self.font_size, - on_render_fg: Some(render), - ..Control::new() - }); - } - - &mut self.canvas.controls[idx] - } -} diff --git a/src/gui/canvas/control.rs b/src/gui/canvas/control.rs deleted file mode 100644 index a122de7..0000000 --- a/src/gui/canvas/control.rs +++ /dev/null @@ -1,387 +0,0 @@ -use glam::Vec4; -use std::sync::Arc; -use vulkano::image::view::ImageView; - -use crate::{ - backend::input::PointerMode, graphics::WlxCommandBuffer, gui::GuiColor, state::AppState, -}; - -use super::{CanvasData, Rect}; - -pub type ControlRenderer = - fn(&Control, &CanvasData, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>; - -pub type ControlRendererHl = fn( - &Control, - &CanvasData, - &mut AppState, - &mut WlxCommandBuffer, - Vec4, -) -> anyhow::Result<()>; - -#[allow(clippy::type_complexity)] -pub struct Control { - pub state: Option, - pub rect: Rect, - pub corner_radius: f32, - pub fg_color: GuiColor, - pub bg_color: GuiColor, - pub text: Arc, - pub size: isize, - pub sprite: Option>, - pub sprite_st: Vec4, - pub(super) bg_dirty: bool, - pub(super) fg_dirty: bool, - - pub on_update: Option, - pub on_press: Option, - pub on_release: Option, - pub on_scroll: Option, - pub test_highlight: Option Option>, - - pub(super) on_render_bg: Option>, - pub(super) on_render_hl: Option>, - pub(super) on_render_fg: Option>, -} - -impl Control { - pub(super) fn new() -> Self { - Self { - rect: Rect { - x: 0., - y: 0., - w: 0., - h: 0., - }, - corner_radius: 0., - fg_color: Vec4::ONE, - bg_color: Vec4::ZERO, - text: Arc::from(""), - sprite: None, - sprite_st: Vec4::new(1., 1., 0., 0.), - bg_dirty: true, - fg_dirty: true, - size: 24, - state: None, - on_update: None, - on_render_bg: None, - on_render_hl: None, - on_render_fg: None, - test_highlight: None, - on_press: None, - on_release: None, - on_scroll: None, - } - } - - pub fn set_text(&mut self, text: &str) { - if *self.text == *text { - return; - } - self.text = text.into(); - self.fg_dirty = true; - } - - pub fn set_sprite(&mut self, sprite: Arc) { - self.sprite.replace(sprite); - self.bg_dirty = true; - } - - pub fn set_sprite_st(&mut self, sprite_st: Vec4) { - if self.sprite_st == sprite_st { - return; - } - self.sprite_st = sprite_st; - self.bg_dirty = true; - } - - pub fn set_fg_color(&mut self, color: GuiColor) { - if self.fg_color == color { - return; - } - self.fg_color = color; - self.fg_dirty = true; - } - - pub fn render_rounded_rect( - &self, - canvas: &CanvasData, - _: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, - ) -> anyhow::Result<()> { - let pass = { - let vertex_buffer = canvas.graphics.upload_verts( - canvas.width as _, - canvas.height as _, - self.rect.x, - self.rect.y, - self.rect.w, - self.rect.h, - )?; - - let clamped_radius = self - .corner_radius - .min(self.rect.w / 2.0) - .min(self.rect.h / 2.0); - - let skew_radius = [clamped_radius / self.rect.w, clamped_radius / self.rect.h]; - - let set0 = canvas.pipeline_bg_color.uniform_buffer( - 0, - vec![ - self.bg_color.x, - self.bg_color.y, - self.bg_color.z, - self.bg_color.w, - skew_radius[0], - skew_radius[1], - ], - )?; - - canvas.pipeline_bg_color.create_pass( - [canvas.width as _, canvas.height as _], - vertex_buffer, - canvas.graphics.quad_indices.clone(), - vec![set0], - )? - }; - - cmd_buffer.run_ref(&pass) - } - - pub(super) fn render_highlight( - &self, - canvas: &CanvasData, - _: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, - color: GuiColor, - ) -> anyhow::Result<()> { - let vertex_buffer = canvas.graphics.upload_verts( - canvas.width as _, - canvas.height as _, - self.rect.x, - self.rect.y, - self.rect.w, - self.rect.h, - )?; - - let clamped_radius = self - .corner_radius - .min(self.rect.w / 2.0) - .min(self.rect.h / 2.0); - - let skew_radius = [clamped_radius / self.rect.w, clamped_radius / self.rect.h]; - - let set0 = canvas.pipeline_hl_color.uniform_buffer( - 0, - vec![ - color.x, - color.y, - color.z, - color.w, - skew_radius[0], - skew_radius[1], - ], - )?; - - let pass = canvas.pipeline_hl_color.create_pass( - [canvas.width as _, canvas.height as _], - vertex_buffer, - canvas.graphics.quad_indices.clone(), - vec![set0], - )?; - - cmd_buffer.run_ref(&pass) - } - - pub(super) fn render_text( - &self, - canvas: &CanvasData, - app: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, - ) -> anyhow::Result<()> { - let mut cur_y = self.rect.y; - for line in self.text.lines() { - let mut cur_x = self.rect.x; - for glyph in app - .fc - .get_glyphs(line, self.size, canvas.graphics.clone())? - { - if let Some(tex) = glyph.tex.clone() { - let vertex_buffer = canvas.graphics.upload_verts( - canvas.width as _, - canvas.height as _, - cur_x + glyph.left, - cur_y - glyph.top, - glyph.width, - glyph.height, - )?; - let set0 = canvas.pipeline_fg_glyph.uniform_sampler( - 0, - ImageView::new_default(tex)?, - app.graphics.texture_filtering, - )?; - let set1 = canvas - .pipeline_fg_glyph - .uniform_buffer(1, self.fg_color.to_array().to_vec())?; - let pass = canvas.pipeline_fg_glyph.create_pass( - [canvas.width as _, canvas.height as _], - vertex_buffer, - canvas.graphics.quad_indices.clone(), - vec![set0, set1], - )?; - cmd_buffer.run_ref(&pass)?; - } - cur_x += glyph.advance; - } - cur_y += (self.size as f32) * 1.5; - } - Ok(()) - } - - pub(super) fn render_text_centered( - &self, - canvas: &CanvasData, - app: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, - ) -> anyhow::Result<()> { - let (w, h) = app - .fc - .get_text_size(&self.text, self.size, canvas.graphics.clone())?; - - let mut cur_y = - (self.size as f32).mul_add(-0.25, h.mul_add(-0.5, self.rect.y + (self.rect.h))); - for line in self.text.lines() { - let mut cur_x = w.mul_add(-0.5, self.rect.w.mul_add(0.5, self.rect.x)); - for glyph in app - .fc - .get_glyphs(line, self.size, canvas.graphics.clone())? - { - if let Some(tex) = glyph.tex.clone() { - let vertex_buffer = canvas.graphics.upload_verts( - canvas.width as _, - canvas.height as _, - cur_x + glyph.left, - cur_y - glyph.top, - glyph.width, - glyph.height, - )?; - let set0 = canvas.pipeline_fg_glyph.uniform_sampler( - 0, - ImageView::new_default(tex)?, - app.graphics.texture_filtering, - )?; - let set1 = canvas - .pipeline_fg_glyph - .uniform_buffer(1, self.fg_color.to_array().to_vec())?; - let pass = canvas.pipeline_fg_glyph.create_pass( - [canvas.width as _, canvas.height as _], - vertex_buffer, - canvas.graphics.quad_indices.clone(), - vec![set0, set1], - )?; - cmd_buffer.run_ref(&pass)?; - } - cur_x += glyph.advance; - } - cur_y += (self.size as f32) * 1.5; - } - Ok(()) - } - - pub(super) fn render_sprite_bg( - &self, - canvas: &CanvasData, - app: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, - ) -> anyhow::Result<()> { - let Some(view) = self.sprite.as_ref() else { - return Ok(()); - }; - - let vertex_buffer = canvas.graphics.upload_verts( - canvas.width as _, - canvas.height as _, - self.rect.x, - self.rect.y, - self.rect.w, - self.rect.h, - )?; - let set0 = canvas.pipeline_bg_sprite.uniform_sampler( - 0, - view.clone(), - app.graphics.texture_filtering, - )?; - - let uniform = vec![ - self.sprite_st.x, - self.sprite_st.y, - self.sprite_st.z, - self.sprite_st.w, - self.fg_color.x, - self.fg_color.y, - self.fg_color.z, - self.fg_color.w, - ]; - - let set1 = canvas.pipeline_bg_sprite.uniform_buffer(1, uniform)?; - - let pass = canvas.pipeline_bg_sprite.create_pass( - [canvas.width as _, canvas.height as _], - vertex_buffer, - canvas.graphics.quad_indices.clone(), - vec![set0, set1], - )?; - cmd_buffer.run_ref(&pass)?; - Ok(()) - } - - #[allow(dead_code)] - pub(super) fn render_sprite_hl( - &self, - canvas: &CanvasData, - app: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, - color: GuiColor, - ) -> anyhow::Result<()> { - let Some(view) = self.sprite.as_ref() else { - return Ok(()); - }; - - let vertex_buffer = canvas.graphics.upload_verts( - canvas.width as _, - canvas.height as _, - self.rect.x, - self.rect.y, - self.rect.w, - self.rect.h, - )?; - let set0 = canvas.pipeline_hl_sprite.uniform_sampler( - 0, - view.clone(), - app.graphics.texture_filtering, - )?; - - let uniform = vec![ - self.sprite_st.x, - self.sprite_st.y, - self.sprite_st.z, - self.sprite_st.w, - color.x, - color.y, - color.z, - color.w, - ]; - - let set1 = canvas.pipeline_hl_sprite.uniform_buffer(1, uniform)?; - - let pass = canvas.pipeline_hl_sprite.create_pass( - [canvas.width as _, canvas.height as _], - vertex_buffer, - canvas.graphics.quad_indices.clone(), - vec![set0, set1], - )?; - cmd_buffer.run_ref(&pass)?; - Ok(()) - } -} diff --git a/src/gui/canvas/mod.rs b/src/gui/canvas/mod.rs deleted file mode 100644 index ae5e837..0000000 --- a/src/gui/canvas/mod.rs +++ /dev/null @@ -1,371 +0,0 @@ -pub mod builder; -pub mod control; - -use std::sync::Arc; - -use glam::{Vec2, Vec4}; -use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::view::ImageView}; - -use crate::{ - backend::{ - input::{Haptics, InteractionHandler, PointerHit}, - overlay::{FrameMeta, OverlayBackend, OverlayRenderer, ShouldRender}, - }, - graphics::{CommandBuffers, WlxGraphics, WlxPipeline, BLEND_ALPHA}, - state::AppState, -}; - -const RES_DIVIDER: usize = 4; - -pub struct Rect { - x: f32, - y: f32, - w: f32, - h: f32, -} - -pub struct CanvasData { - pub data: D, - pub width: usize, - pub height: usize, - - graphics: Arc, - - pipeline_bg_color: Arc, - pipeline_bg_sprite: Arc, - pipeline_fg_glyph: Arc, - pipeline_hl_color: Arc, - pipeline_hl_sprite: Arc, -} - -pub struct Canvas { - controls: Vec>, - data: CanvasData, - - hover_controls: [Option; 2], - pressed_controls: [Option; 2], - - interact_map: Vec>, - interact_stride: usize, - interact_rows: usize, - - pipeline_final: Arc, - - view_fore: Arc, - view_back: Arc, - - format: Format, - - back_dirty: bool, - high_dirty: bool, - fore_dirty: bool, -} - -impl Canvas { - fn new( - width: usize, - height: usize, - graphics: Arc, - format: Format, - data: D, - ) -> anyhow::Result { - let tex_fore = graphics.render_texture(width as _, height as _, format)?; - let tex_back = graphics.render_texture(width as _, height as _, format)?; - - let view_fore = ImageView::new_default(tex_fore)?; - let view_back = ImageView::new_default(tex_back)?; - - let Ok(shaders) = graphics.shared_shaders.read() else { - anyhow::bail!("Failed to lock shared shaders for reading"); - }; - - let vert = shaders.get("vert_common").unwrap().clone(); // want panic - - let pipeline_bg_color = graphics.create_pipeline( - vert.clone(), - shaders.get("frag_color").unwrap().clone(), // want panic - format, - Some(BLEND_ALPHA), - )?; - - let pipeline_fg_glyph = graphics.create_pipeline( - vert.clone(), - shaders.get("frag_glyph").unwrap().clone(), // want panic - format, - Some(BLEND_ALPHA), - )?; - - let pipeline_bg_sprite = graphics.create_pipeline( - vert.clone(), - shaders.get("frag_sprite2").unwrap().clone(), // want panic - format, - Some(BLEND_ALPHA), - )?; - - let pipeline_hl_color = graphics.create_pipeline( - vert.clone(), - shaders.get("frag_color").unwrap().clone(), // want panic - graphics.native_format, - Some(BLEND_ALPHA), - )?; - - let pipeline_hl_sprite = graphics.create_pipeline( - vert.clone(), - shaders.get("frag_sprite2_hl").unwrap().clone(), // want panic - graphics.native_format, - Some(BLEND_ALPHA), - )?; - - let pipeline_final = graphics.create_pipeline( - vert, - shaders.get("frag_srgb").unwrap().clone(), // want panic - graphics.native_format, - Some(BLEND_ALPHA), - )?; - - let stride = width / RES_DIVIDER; - let rows = height / RES_DIVIDER; - - Ok(Self { - data: CanvasData { - data, - width, - height, - graphics: graphics.clone(), - pipeline_bg_color, - pipeline_bg_sprite, - pipeline_fg_glyph, - pipeline_hl_color, - pipeline_hl_sprite, - }, - controls: Vec::new(), - hover_controls: [None, None], - pressed_controls: [None, None], - interact_map: vec![None; stride * rows], - interact_stride: stride, - interact_rows: rows, - pipeline_final, - view_fore, - view_back, - format, - back_dirty: false, - high_dirty: false, - fore_dirty: false, - }) - } - - fn interactive_set_idx(&mut self, x: f32, y: f32, w: f32, h: f32, idx: usize) { - let (x, y, w, h) = (x as usize, y as usize, w as usize, h as usize); - - let x_min = (x / RES_DIVIDER).max(0); - let y_min = (y / RES_DIVIDER).max(0); - let x_max = (x_min + (w / RES_DIVIDER)).min(self.interact_stride - 1); - let y_max = (y_min + (h / RES_DIVIDER)).min(self.interact_rows - 1); - - for y in y_min..y_max { - for x in x_min..x_max { - self.interact_map[y * self.interact_stride + x] = Some(idx as u16); - } - } - } - - fn interactive_get_idx(&self, uv: Vec2) -> Option { - let x = (uv.x * self.data.width as f32) as usize; - let y = (uv.y * self.data.height as f32) as usize; - let x = (x / RES_DIVIDER).max(0).min(self.interact_stride - 1); - let y = (y / RES_DIVIDER).max(0).min(self.interact_rows - 1); - self.interact_map[y * self.interact_stride + x].map(|x| x as usize) - } - - pub const fn data_mut(&mut self) -> &mut D { - &mut self.data.data - } -} - -impl InteractionHandler for Canvas { - fn on_left(&mut self, _app: &mut AppState, pointer: usize) { - self.high_dirty = true; - - self.hover_controls[pointer] = None; - } - fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option { - // render on every frame if we are being hovered - self.high_dirty = true; - - let old = self.hover_controls[hit.pointer]; - if let Some(i) = self.interactive_get_idx(hit.uv) { - self.hover_controls[hit.pointer] = Some(i); - } else { - self.hover_controls[hit.pointer] = None; - } - if old == self.hover_controls[hit.pointer] { - None - } else { - Some(Haptics { - intensity: 0.1, - duration: 0.01, - frequency: 5.0, - }) - } - } - fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { - let idx = if pressed { - self.interactive_get_idx(hit.uv) - } else { - self.pressed_controls[hit.pointer] - }; - - if let Some(idx) = idx { - let c = &mut self.controls[idx]; - if pressed { - if let Some(ref mut f) = c.on_press { - self.pressed_controls[hit.pointer] = Some(idx); - f(c, &mut self.data.data, app, hit.mode); - } - } else if let Some(ref mut f) = c.on_release { - self.pressed_controls[hit.pointer] = None; - f(c, &mut self.data.data, app); - } - } - } - fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) { - let idx = self.hover_controls[hit.pointer]; - - if let Some(idx) = idx { - let c = &mut self.controls[idx]; - if let Some(ref mut f) = c.on_scroll { - f(c, &mut self.data.data, app, delta_y, delta_x); - } - } - } -} - -impl OverlayRenderer for Canvas { - fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { - for c in &mut self.controls { - if let Some(fun) = c.on_update { - fun(c, &mut self.data.data, app); - } - if c.fg_dirty { - self.fore_dirty = true; - c.fg_dirty = false; - } - if c.bg_dirty { - self.back_dirty = true; - c.bg_dirty = false; - } - } - - if self.back_dirty || self.fore_dirty || self.high_dirty { - Ok(ShouldRender::Should) - } else { - Ok(ShouldRender::Can) - } - } - fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result { - self.high_dirty = false; - - let mut cmd_buffer = self - .data - .graphics - .create_command_buffer(CommandBufferUsage::OneTimeSubmit)?; - - if self.back_dirty { - cmd_buffer.begin_rendering(self.view_back.clone())?; - for c in &mut self.controls { - if let Some(fun) = c.on_render_bg { - fun(c, &self.data, app, &mut cmd_buffer)?; - } - } - cmd_buffer.end_rendering()?; - self.back_dirty = false; - } - if self.fore_dirty { - cmd_buffer.begin_rendering(self.view_fore.clone())?; - for c in &mut self.controls { - if let Some(fun) = c.on_render_fg { - fun(c, &self.data, app, &mut cmd_buffer)?; - } - } - cmd_buffer.end_rendering()?; - self.fore_dirty = false; - } - - let set0_fg = self.pipeline_final.uniform_sampler( - 0, - self.view_fore.clone(), - app.graphics.texture_filtering, - )?; - let set0_bg = self.pipeline_final.uniform_sampler( - 0, - self.view_back.clone(), - app.graphics.texture_filtering, - )?; - let set1 = self.pipeline_final.uniform_buffer(1, vec![alpha])?; - - let pass_fore = self - .pipeline_final - .create_pass_for_target(tgt.clone(), vec![set0_fg, set1.clone()])?; - - let pass_back = self - .pipeline_final - .create_pass_for_target(tgt.clone(), vec![set0_bg, set1])?; - - cmd_buffer.begin_rendering(tgt)?; - cmd_buffer.run_ref(&pass_back)?; - - for (i, c) in self.controls.iter_mut().enumerate() { - if let Some(render) = c.on_render_hl { - if let Some(test) = c.test_highlight { - if let Some(hl_color) = test(c, &mut self.data.data, app) { - render(c, &self.data, app, &mut cmd_buffer, hl_color)?; - } - } - if self.hover_controls.contains(&Some(i)) { - render( - c, - &self.data, - app, - &mut cmd_buffer, - Vec4::new(1., 1., 1., 0.3), - )?; - } - } - } - - // mostly static text - cmd_buffer.run_ref(&pass_fore)?; - - cmd_buffer.end_rendering()?; - buf.push(cmd_buffer.build()?); - Ok(true) - } - - fn frame_meta(&mut self) -> Option { - Some(FrameMeta { - extent: [self.data.width as _, self.data.height as _, 1], - format: self.format, - ..Default::default() - }) - } -} - -impl OverlayBackend for Canvas { - fn set_renderer(&mut self, _renderer: Box) {} - fn set_interaction(&mut self, _interaction: Box) {} -} diff --git a/src/gui/font.rs b/src/gui/font.rs deleted file mode 100644 index 3bb24d9..0000000 --- a/src/gui/font.rs +++ /dev/null @@ -1,268 +0,0 @@ -use std::{rc::Rc, str::FromStr, sync::Arc}; - -use fontconfig::{FontConfig, OwnedPattern}; -use freetype::{bitmap::PixelMode, face::LoadFlag, Face, Library}; -use idmap::IdMap; -use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::Image}; - -use crate::graphics::{WlxGraphics, WlxUploadsBuffer}; - -pub struct FontCache { - primary_font: Arc, - fc: FontConfig, - ft: Library, - collections: IdMap, -} - -struct FontCollection { - fonts: Vec, - cp_map: IdMap, - zero_glyph: Rc, -} - -struct Font { - face: Face, - glyphs: IdMap>, -} - -pub struct Glyph { - pub tex: Option>, - pub top: f32, - pub left: f32, - pub width: f32, - pub height: f32, - pub advance: f32, -} - -impl FontCache { - pub fn new(primary_font: Arc) -> anyhow::Result { - let ft = Library::init()?; - let fc = FontConfig::default(); - - Ok(Self { - primary_font, - fc, - ft, - collections: IdMap::new(), - }) - } - - pub fn get_text_size( - &mut self, - text: &str, - size: isize, - graphics: Arc, - ) -> anyhow::Result<(f32, f32)> { - let sizef = size as f32; - - let height = ((text.lines().count() as f32) - 1f32).mul_add(sizef * 1.5, sizef); - let mut cmd_buffer = None; - - let mut max_w = sizef * 0.33; - for line in text.lines() { - let w: f32 = line - .chars() - .filter_map(|c| { - self.get_glyph_for_cp(c as usize, size, graphics.clone(), &mut cmd_buffer) - .map(|glyph| glyph.advance) - .ok() - }) - .sum(); - - if w > max_w { - max_w = w; - } - } - - if let Some(cmd_buffer) = cmd_buffer { - cmd_buffer.build_and_execute_now()?; - } - - Ok((max_w, height)) - } - - pub fn get_glyphs( - &mut self, - text: &str, - size: isize, - graphics: Arc, - ) -> anyhow::Result>> { - let mut glyphs = Vec::new(); - let mut cmd_buffer = None; - - for line in text.lines() { - for c in line.chars() { - glyphs.push(self.get_glyph_for_cp( - c as usize, - size, - graphics.clone(), - &mut cmd_buffer, - )?); - } - } - - if let Some(cmd_buffer) = cmd_buffer { - cmd_buffer.build_and_execute_now()?; - } - - Ok(glyphs) - } - - fn get_font_for_cp(&mut self, cp: usize, size: isize) -> usize { - if !self.collections.contains_key(size) { - self.collections.insert( - size, - FontCollection { - fonts: Vec::new(), - cp_map: IdMap::new(), - zero_glyph: Rc::new(Glyph { - tex: None, - top: 0., - left: 0., - width: 0., - height: 0., - advance: size as f32 / 3., - }), - }, - ); - } - let coll = self.collections.get_mut(size).unwrap(); // safe because of the insert above - - if let Some(font) = coll.cp_map.get(cp) { - return *font; - } - - let primary_font = self.primary_font.clone(); - let pattern_str = format!("{primary_font}:size={size}:charset={cp:04x}"); - let mut pattern = OwnedPattern::from_str(&pattern_str).unwrap(); // safe because PRIMARY_FONT is const - self.fc - .substitute(&mut pattern, fontconfig::MatchKind::Pattern); - pattern.default_substitute(); - - let pattern = pattern.font_match(&mut self.fc); - - if let Some(path) = pattern.filename() { - let name = pattern.name().unwrap_or(path); - log::debug!("Loading font: {name} {size}pt"); - - let font_idx = pattern.face_index().unwrap_or(0); - - let face = match self.ft.new_face(path, font_idx as _) { - Ok(face) => face, - Err(e) => { - log::warn!("Failed to load font at {path}: {e:?}"); - coll.cp_map.insert(cp, 0); - return 0; - } - }; - match face.set_char_size(size << 6, size << 6, 96, 96) { - Ok(()) => {} - Err(e) => { - log::warn!("Failed to set font size: {e:?}"); - coll.cp_map.insert(cp, 0); - return 0; - } - } - - let idx = coll.fonts.len(); - for (cp, _) in face.chars() { - if coll.cp_map.contains_key(cp) { - continue; - } - coll.cp_map.insert(cp, idx); - } - - if !coll.cp_map.contains_key(cp) { - log::warn!("Got font '{name}' for CP 0x{cp:x}, but CP is not present in font!",); - coll.cp_map.insert(cp, 0); - } - - let zero_glyph = Rc::new(Glyph { - tex: None, - top: 0., - left: 0., - width: 0., - height: 0., - advance: size as f32 / 3., - }); - let mut glyphs = IdMap::new(); - glyphs.insert(0, zero_glyph); - - let font = Font { face, glyphs }; - coll.fonts.push(font); - - return idx; - } - coll.cp_map.insert(cp, 0); - 0 - } - - fn get_glyph_for_cp( - &mut self, - cp: usize, - size: isize, - graphics: Arc, - cmd_buffer: &mut Option, - ) -> anyhow::Result> { - let key = self.get_font_for_cp(cp, size); - - let Some(font) = &mut self.collections[size].fonts.get_mut(key) else { - log::warn!("No font found for codepoint: 0x{cp:x}"); - return Ok(self.collections[size].zero_glyph.clone()); - }; - - if let Some(glyph) = font.glyphs.get(cp) { - return Ok(glyph.clone()); - } - - if font.face.load_char(cp, LoadFlag::DEFAULT).is_err() { - return Ok(self.collections[size].zero_glyph.clone()); - } - - let glyph = font.face.glyph(); - if glyph.render_glyph(freetype::RenderMode::Normal).is_err() { - return Ok(self.collections[size].zero_glyph.clone()); - } - - let bmp = glyph.bitmap(); - let buf = bmp.buffer().to_vec(); - if buf.is_empty() { - return Ok(self.collections[size].zero_glyph.clone()); - } - - let metrics = glyph.metrics(); - - let format = match bmp.pixel_mode() { - Ok(PixelMode::Gray) => Format::R8_UNORM, - Ok(PixelMode::Gray2) => Format::R16_SFLOAT, - Ok(PixelMode::Gray4) => Format::R32_SFLOAT, - _ => return Ok(self.collections[size].zero_glyph.clone()), - }; - - if cmd_buffer.is_none() { - *cmd_buffer = Some(graphics.create_uploads_command_buffer( - graphics.transfer_queue.clone(), - CommandBufferUsage::OneTimeSubmit, - )?); - } - - let texture = cmd_buffer.as_mut().unwrap().texture2d_raw( - bmp.width() as _, - bmp.rows() as _, - format, - &buf, - )?; - - let g = Glyph { - tex: Some(texture), - top: (metrics.horiBearingY >> 6i64) as _, - left: (metrics.horiBearingX >> 6i64) as _, - advance: (metrics.horiAdvance >> 6i64) as _, - width: bmp.width() as _, - height: bmp.rows() as _, - }; - - font.glyphs.insert(cp, Rc::new(g)); - Ok(font.glyphs[cp].clone()) - } -} diff --git a/src/gui/mod.rs b/src/gui/mod.rs deleted file mode 100644 index 939f28f..0000000 --- a/src/gui/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::sync::LazyLock; - -use glam::Vec4; - -pub mod canvas; -pub mod font; -pub mod modular; - -pub type GuiColor = Vec4; -pub static FALLBACK_COLOR: LazyLock = LazyLock::new(|| Vec4::new(1., 0., 1., 1.)); - -// Parses a color from a HTML hex string -pub fn color_parse(html_hex: &str) -> anyhow::Result { - if html_hex.len() == 7 { - if let (Ok(r), Ok(g), Ok(b)) = ( - u8::from_str_radix(&html_hex[1..3], 16), - u8::from_str_radix(&html_hex[3..5], 16), - u8::from_str_radix(&html_hex[5..7], 16), - ) { - return Ok(Vec4::new( - f32::from(r) / 255., - f32::from(g) / 255., - f32::from(b) / 255., - 1., - )); - } - } else if html_hex.len() == 9 { - if let (Ok(r), Ok(g), Ok(b), Ok(a)) = ( - u8::from_str_radix(&html_hex[1..3], 16), - u8::from_str_radix(&html_hex[3..5], 16), - u8::from_str_radix(&html_hex[5..7], 16), - u8::from_str_radix(&html_hex[7..9], 16), - ) { - return Ok(Vec4::new( - f32::from(r) / 255., - f32::from(g) / 255., - f32::from(b) / 255., - f32::from(a) / 255., - )); - } - } - anyhow::bail!( - "Invalid color string: '{}', must be 7 characters long (e.g. #FF00FF)", - &html_hex - ) -} - -pub enum KeyCapType { - /// Label is in center of keycap - Regular, - /// Label on the top - /// AltGr symbol on bottom - RegularAltGr, - /// Primary symbol on bottom - /// Shift symbol on top - Reversed, - /// Primary symbol on bottom-left - /// Shift symbol on top-left - /// AltGr symbol on bottom-right - ReversedAltGr, -} diff --git a/src/gui/modular/button.rs b/src/gui/modular/button.rs deleted file mode 100644 index 44bd062..0000000 --- a/src/gui/modular/button.rs +++ /dev/null @@ -1,867 +0,0 @@ -use std::{ - f32::consts::PI, - ops::Add, - process::{self, Child}, - sync::Arc, - time::{Duration, Instant}, -}; - -use glam::{Quat, Vec4}; -use serde::Deserialize; - -use crate::{ - backend::{ - common::OverlaySelector, - input::PointerMode, - overlay::Positioning, - task::{ColorChannel, SystemTask, TaskType}, - }, - config::{save_layout, save_settings, AStrSetExt}, - hid::VirtualKey, - overlays::{ - toast::{error_toast, Toast, ToastTopic}, - watch::WATCH_NAME, - }, - state::AppState, -}; - -#[cfg(any(not(feature = "wayvr"), not(feature = "osc")))] -use crate::overlays::toast::error_toast_str; - -#[cfg(feature = "osc")] -use rosc::OscType; - -use super::{ExecArgs, ModularControl, ModularData}; - -#[derive(Deserialize, Clone)] -pub enum PressRelease { - Release, - Press, -} - -#[derive(Deserialize, Clone, Copy)] -pub enum ViewAngleKind { - /// The cosine of the angle at which the watch becomes fully transparent - MinOpacity, - /// The cosine of the angle at which the watch becomes fully opaque - MaxOpacity, -} - -#[derive(Deserialize, Clone, Copy)] -pub enum Axis { - X, - Y, - Z, -} - -#[derive(Deserialize, Clone)] -pub enum HighlightTest { - AllowSliding, - AutoRealign, - NotificationSounds, - Notifications, - RorateLock, -} - -#[derive(Deserialize, Clone)] -pub enum SystemAction { - ToggleAllowSliding, - ToggleAutoRealign, - ToggleNotificationSounds, - ToggleNotifications, - ToggleRotateLock, - PlayspaceResetOffset, - PlayspaceFixFloor, - RecalculateExtent, - PersistConfig, - PersistLayout, -} - -#[derive(Deserialize, Clone)] -pub enum WatchAction { - /// Hide the watch until Show/Hide binding is used - Hide, - /// Switch the watch to the opposite controller - SwitchHands, - /// Change the fade behavior of the watch - ViewAngle { - kind: ViewAngleKind, - delta: f32, - }, - Rotation { - axis: Axis, - delta: f32, - }, - Position { - axis: Axis, - delta: f32, - }, -} - -#[derive(Deserialize, Clone)] -pub enum OverlayAction { - /// Reset the overlay to be in front of the HMD with its original scale - Reset, - /// Toggle the visibility of the overlay - ToggleVisible, - /// Toggle the ability to grab and recenter the overlay - ToggleImmovable, - /// Toggle the ability of the overlay to reacto to laser pointer - ToggleInteraction, - /// Change the opacity of the overlay - Opacity { delta: f32 }, -} - -#[derive(Deserialize, Clone)] -pub enum WindowAction { - /// Create a new mirror window, or show/hide an existing one - ShowMirror, - /// Create a new UI window, or show/hide an existing one - ShowUi, - /// Destroy a previously created window, if it exists - Destroy, -} - -#[derive(Deserialize, Clone)] -pub enum WayVRDisplayClickAction { - ToggleVisibility, - Reset, -} - -#[derive(Deserialize, Clone)] -#[allow(dead_code)] // in case if WayVR feature is disabled -pub enum WayVRAction { - AppClick { - catalog_name: Arc, - app_name: Arc, - }, - DisplayClick { - display_name: Arc, - action: WayVRDisplayClickAction, - }, - ToggleDashboard, -} - -#[derive(Deserialize, Clone)] -#[serde(tag = "type")] -pub enum ButtonAction { - Exec { - command: ExecArgs, - toast: Option>, - }, - VirtualKey { - keycode: VirtualKey, - action: PressRelease, - }, - Watch { - action: WatchAction, - }, - Overlay { - target: OverlaySelector, - action: OverlayAction, - }, - // Ignored if "wayvr" feature is not enabled - WayVR { - action: WayVRAction, - }, - Window { - target: Arc, - action: WindowAction, - }, - Toast { - message: Arc, - body: Option>, - seconds: Option, - }, - ColorAdjust { - channel: ColorChannel, - delta: f32, - }, - DragMultiplier { - delta: f32, - }, - System { - action: SystemAction, - }, - SendOscValue { - parameter: Arc, - values: Option>, - }, -} - -#[derive(Deserialize, Clone)] -#[serde(tag = "type")] -#[cfg(feature = "osc")] -pub enum OscValue { - Int { value: i32 }, - Float { value: f32 }, - String { value: String }, - Bool { value: bool }, -} -#[derive(Deserialize, Clone)] -#[serde(tag = "type")] -#[cfg(not(feature = "osc"))] -pub enum OscValue { - None, -} - -pub(super) struct PressData { - last_down: Instant, - last_mode: PointerMode, - child: Option, -} -impl Clone for PressData { - fn clone(&self) -> Self { - Self { - last_down: self.last_down, - last_mode: self.last_mode, - child: None, - } - } -} -impl Default for PressData { - fn default() -> Self { - Self { - last_down: Instant::now(), - last_mode: PointerMode::Left, - child: None, - } - } -} - -#[derive(Deserialize, Default, Clone)] -pub struct ButtonData { - #[serde(skip)] - pub(super) press: PressData, - - pub(super) click_down: Option>, - pub(super) click_up: Option>, - pub(super) long_click_up: Option>, - pub(super) right_down: Option>, - pub(super) right_up: Option>, - pub(super) long_right_up: Option>, - pub(super) middle_down: Option>, - pub(super) middle_up: Option>, - pub(super) long_middle_up: Option>, - pub(super) scroll_down: Option>, - pub(super) scroll_up: Option>, - pub(super) highlight: Option, -} - -pub fn modular_button_init(button: &mut ModularControl, data: &ButtonData) { - button.state = Some(ModularData::Button(Box::new(data.clone()))); - button.on_press = Some(modular_button_dn); - button.on_release = Some(modular_button_up); - button.on_scroll = Some(modular_button_scroll); - button.test_highlight = Some(modular_button_highlight); -} - -fn modular_button_dn( - button: &mut ModularControl, - _: &mut (), - app: &mut AppState, - mode: PointerMode, -) { - // want panic - let ModularData::Button(data) = button.state.as_mut().unwrap() else { - panic!("modular_button_dn: button state is not Button"); - }; - - data.press.last_down = Instant::now(); - data.press.last_mode = mode; - - let actions = match mode { - PointerMode::Left => data.click_down.as_ref(), - PointerMode::Right => data.right_down.as_ref(), - PointerMode::Middle => data.middle_down.as_ref(), - _ => None, - }; - - if let Some(actions) = actions { - for action in actions { - handle_action(action, &mut data.press, app); - } - } -} - -fn modular_button_up(button: &mut ModularControl, _: &mut (), app: &mut AppState) { - // want panic - let ModularData::Button(data) = button.state.as_mut().unwrap() else { - panic!("modular_button_up: button state is not Button"); - }; - - let now = Instant::now(); - let duration = now - data.press.last_down; - let long_press = duration.as_secs_f32() > app.session.config.long_press_duration; - - let actions = match data.press.last_mode { - PointerMode::Left => { - if long_press { - data.long_click_up.as_ref() - } else { - data.click_up.as_ref() - } - } - PointerMode::Right => { - if long_press { - data.long_right_up.as_ref() - } else { - data.right_up.as_ref() - } - } - PointerMode::Middle => { - if long_press { - data.long_middle_up.as_ref() - } else { - data.middle_up.as_ref() - } - } - _ => None, - }; - - if let Some(actions) = actions { - for action in actions { - handle_action(action, &mut data.press, app); - } - } -} - -fn modular_button_scroll( - button: &mut ModularControl, - _: &mut (), - app: &mut AppState, - delta_y: f32, - _delta_x: f32, -) { - // want panic - let ModularData::Button(data) = button.state.as_mut().unwrap() else { - panic!("modular_button_scroll: button state is not Button"); - }; - - let actions = if delta_y < 0.0 { - data.scroll_down.as_ref() - } else { - data.scroll_up.as_ref() - }; - - if let Some(actions) = actions { - for action in actions { - handle_action(action, &mut data.press, app); - } - } -} - -fn modular_button_highlight( - button: &ModularControl, - _: &mut (), - app: &mut AppState, -) -> Option { - // want panic - let ModularData::Button(data) = button.state.as_ref().unwrap() else { - panic!("modular_button_highlight: button state is not Button"); - }; - - if let Some(test) = &data.highlight { - let lit = match test { - HighlightTest::AllowSliding => app.session.config.allow_sliding, - HighlightTest::AutoRealign => app.session.config.realign_on_showhide, - HighlightTest::NotificationSounds => app.session.config.notifications_sound_enabled, - HighlightTest::Notifications => app.session.config.notifications_enabled, - HighlightTest::RorateLock => !app.session.config.space_rotate_unlocked, - }; - - if lit { - return Some(Vec4::new(1.0, 1.0, 1.0, 0.5)); - } - } - None -} - -fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppState) { - match action { - ButtonAction::Exec { command, toast } => run_exec(command, toast.clone(), press, app), - ButtonAction::Watch { action } => run_watch(action, app), - ButtonAction::Overlay { target, action } => run_overlay(target, action, app), - ButtonAction::Window { target, action } => run_window(target, action, app), - ButtonAction::WayVR { action } => { - #[cfg(feature = "wayvr")] - { - app.tasks.enqueue(TaskType::WayVR(action.clone())); - } - #[cfg(not(feature = "wayvr"))] - { - let _ = &action; - error_toast_str(app, "WayVR feature is not enabled"); - } - } - ButtonAction::VirtualKey { keycode, action } => app - .hid_provider - .send_key(*keycode, matches!(*action, PressRelease::Press)), - ButtonAction::Toast { - message, - body, - seconds, - } => { - Toast::new( - ToastTopic::System, - message.clone(), - body.clone().unwrap_or_else(|| "".into()), - ) - .with_timeout(seconds.unwrap_or(5.)) - .submit(app); - } - ButtonAction::ColorAdjust { channel, delta } => { - let channel = *channel; - let delta = *delta; - app.tasks - .enqueue(TaskType::System(SystemTask::ColorGain(channel, delta))); - } - ButtonAction::System { action } => run_system(action, app), - ButtonAction::DragMultiplier { delta } => { - app.session.config.space_drag_multiplier += delta; - } - ButtonAction::SendOscValue { parameter, values } => { - #[cfg(feature = "osc")] - if let Some(ref mut sender) = app.osc_sender { - // convert OscValue to OscType - let mut converted: Vec = Vec::new(); - - for value in values.as_ref().unwrap() { - let converted_value = match value { - OscValue::Bool { value } => OscType::Bool(*value), - OscValue::Int { value } => OscType::Int(*value), - OscValue::Float { value } => OscType::Float(*value), - OscValue::String { value } => OscType::String(value.to_string()), - }; - - converted.push(converted_value); - } - - let _ = sender.send_single_param(parameter.to_string(), converted); - audio_thump(app); // play sound for feedback - } - - #[cfg(not(feature = "osc"))] - { - let _ = ¶meter; - let _ = &values; - error_toast_str(app, "OSC feature is not enabled"); - } - } - } -} - -const ENABLED_DISABLED: [&str; 2] = ["enabled", "disabled"]; - -#[allow(clippy::too_many_lines)] -fn run_system(action: &SystemAction, app: &mut AppState) { - match action { - SystemAction::ToggleAllowSliding => { - app.session.config.allow_sliding = !app.session.config.allow_sliding; - Toast::new( - ToastTopic::System, - format!( - "Sliding is {}.", - ENABLED_DISABLED[usize::from(app.session.config.allow_sliding)] - ) - .into(), - "".into(), - ) - .submit(app); - } - SystemAction::ToggleAutoRealign => { - app.session.config.realign_on_showhide = !app.session.config.realign_on_showhide; - Toast::new( - ToastTopic::System, - format!( - "Auto realign is {}.", - ENABLED_DISABLED[usize::from(app.session.config.realign_on_showhide)] - ) - .into(), - "".into(), - ) - .submit(app); - } - SystemAction::ToggleRotateLock => { - app.session.config.space_rotate_unlocked = !app.session.config.space_rotate_unlocked; - Toast::new( - ToastTopic::System, - format!( - "Space rotate axis lock now {}.", - ENABLED_DISABLED[usize::from(!app.session.config.space_rotate_unlocked)] - ) - .into(), - "".into(), - ) - .submit(app); - } - SystemAction::PlayspaceResetOffset => { - app.tasks - .enqueue(TaskType::System(SystemTask::ResetPlayspace)); - } - SystemAction::PlayspaceFixFloor => { - let now = Instant::now(); - let sec = Duration::from_secs(1); - for i in 0..5 { - let at = now.add(i * sec); - let display = 5 - i; - Toast::new( - ToastTopic::System, - format!("Fixing floor in {display}").into(), - "Place either controller on the floor.".into(), - ) - .with_timeout(1.0) - .submit_at(app, at); - } - app.tasks - .enqueue_at(TaskType::System(SystemTask::FixFloor), now.add(5 * sec)); - } - SystemAction::RecalculateExtent => { - todo!() - } - SystemAction::ToggleNotifications => { - app.session.config.notifications_enabled = !app.session.config.notifications_enabled; - Toast::new( - ToastTopic::System, - format!( - "Notifications are {}.", - ENABLED_DISABLED[usize::from(app.session.config.notifications_enabled)] - ) - .into(), - "".into(), - ) - .submit(app); - } - SystemAction::ToggleNotificationSounds => { - app.session.config.notifications_sound_enabled = - !app.session.config.notifications_sound_enabled; - Toast::new( - ToastTopic::System, - format!( - "Notification sounds are {}.", - ENABLED_DISABLED[usize::from(app.session.config.notifications_sound_enabled)] - ) - .into(), - "".into(), - ) - .submit(app); - } - SystemAction::PersistConfig => { - if let Err(e) = save_settings(&app.session.config) { - error_toast(app, "Failed to save config", e); - } - } - SystemAction::PersistLayout => { - if let Err(e) = save_layout(&app.session.config) { - error_toast(app, "Failed to save layout", e); - } - } - } -} - -fn run_exec(args: &ExecArgs, toast: Option>, press: &mut PressData, app: &mut AppState) { - if let Some(proc) = press.child.as_mut() { - match proc.try_wait() { - Ok(Some(code)) => { - if !code.success() { - error_toast(app, "Child process exited with code", code); - } - press.child = None; - } - Ok(None) => { - log::warn!("Unable to launch child process: previous child not exited yet"); - return; - } - Err(e) => { - press.child = None; - error_toast(app, "Error checking child process", e); - } - } - } - let args = args - .iter() - .map(std::convert::AsRef::as_ref) - .collect::>(); - match process::Command::new(args[0]).args(&args[1..]).spawn() { - Ok(proc) => { - press.child = Some(proc); - if let Some(toast) = toast.as_ref() { - Toast::new(ToastTopic::System, toast.clone(), "".into()).submit(app); - } - } - Err(e) => { - error_toast(app, &format!("Failed to spawn process {args:?}"), e); - } - } -} - -#[allow(clippy::too_many_lines)] -fn run_watch(data: &WatchAction, app: &mut AppState) { - match data { - WatchAction::Hide => { - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(WATCH_NAME.into()), - Box::new(|app, o| { - if o.saved_transform.is_none() { - o.want_visible = false; - o.saved_transform = Some(o.transform); - Toast::new( - ToastTopic::System, - "Watch hidden".into(), - "Use show/hide binding to restore.".into(), - ) - .with_timeout(3.) - .submit(app); - } else { - o.want_visible = true; - o.saved_transform = None; - Toast::new(ToastTopic::System, "Watch restored".into(), "".into()) - .submit(app); - } - }), - )); - audio_thump(app); - } - WatchAction::SwitchHands => { - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(WATCH_NAME.into()), - Box::new(|app, o| { - if matches!(o.positioning, Positioning::FollowHand { hand: 0, .. }) { - o.positioning = Positioning::FollowHand { hand: 1, lerp: 1.0 }; - o.spawn_rotation = app.session.config.watch_rot - * Quat::from_rotation_x(PI) - * Quat::from_rotation_z(PI); - o.spawn_point = app.session.config.watch_pos; - o.spawn_point.x *= -1.; - } else { - o.positioning = Positioning::FollowHand { hand: 0, lerp: 1.0 }; - o.spawn_rotation = app.session.config.watch_rot; - o.spawn_point = app.session.config.watch_pos; - } - o.dirty = true; - Toast::new( - ToastTopic::System, - "Watch switched".into(), - "Check your other hand".into(), - ) - .with_timeout(3.) - .submit(app); - }), - )); - audio_thump(app); - } - WatchAction::ViewAngle { kind, delta } => match kind { - ViewAngleKind::MinOpacity => { - let diff = (app.session.config.watch_view_angle_max - - app.session.config.watch_view_angle_min) - + delta; - - app.session.config.watch_view_angle_min = (app.session.config.watch_view_angle_max - - diff) - .clamp(0.0, app.session.config.watch_view_angle_max - 0.05); - } - ViewAngleKind::MaxOpacity => { - let diff = app.session.config.watch_view_angle_max - - app.session.config.watch_view_angle_min; - - app.session.config.watch_view_angle_max = - (app.session.config.watch_view_angle_max + delta).clamp(0.05, 1.0); - - app.session.config.watch_view_angle_min = (app.session.config.watch_view_angle_max - - diff) - .clamp(0.0, app.session.config.watch_view_angle_max - 0.05); - } - }, - WatchAction::Rotation { axis, delta } => { - let rot = match axis { - Axis::X => Quat::from_rotation_x(delta.to_radians()), - Axis::Y => Quat::from_rotation_y(delta.to_radians()), - Axis::Z => Quat::from_rotation_z(delta.to_radians()), - }; - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(WATCH_NAME.into()), - Box::new(move |app, o| { - o.spawn_rotation *= rot; - app.session.config.watch_rot = o.spawn_rotation; - o.dirty = true; - }), - )); - } - WatchAction::Position { axis, delta } => { - let delta = *delta; - let axis = match axis { - Axis::X => 0, - Axis::Y => 1, - Axis::Z => 2, - }; - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(WATCH_NAME.into()), - Box::new(move |app, o| { - o.spawn_point[axis] += delta; - app.session.config.watch_pos = o.spawn_point; - o.dirty = true; - }), - )); - } - } -} - -#[allow(clippy::too_many_lines)] -fn run_overlay(overlay: &OverlaySelector, action: &OverlayAction, app: &mut AppState) { - match action { - OverlayAction::Reset => { - app.tasks.enqueue(TaskType::Overlay( - overlay.clone(), - Box::new(|app, o| { - o.reset(app, true); - Toast::new( - ToastTopic::System, - format!("{} has been reset!", o.name).into(), - "".into(), - ) - .submit(app); - }), - )); - } - OverlayAction::ToggleVisible => { - app.tasks.enqueue(TaskType::Overlay( - overlay.clone(), - Box::new(|app, o| { - o.want_visible = !o.want_visible; - if o.recenter { - o.show_hide = o.want_visible; - o.reset(app, false); - } - - let mut state_dirty = false; - if !o.want_visible { - state_dirty |= app.session.config.show_screens.arc_rm(o.name.as_ref()); - } else if o.want_visible { - state_dirty |= app.session.config.show_screens.arc_set(o.name.clone()); - } - - if state_dirty { - match save_layout(&app.session.config) { - Ok(()) => log::debug!("Saved state"), - Err(e) => { - error_toast(app, "Failed to save state", e); - } - } - } - }), - )); - } - OverlayAction::ToggleImmovable => { - app.tasks.enqueue(TaskType::Overlay( - overlay.clone(), - Box::new(|app, o| { - o.recenter = !o.recenter; - o.grabbable = o.recenter; - o.show_hide = o.recenter; - if o.recenter { - Toast::new( - ToastTopic::System, - format!("{} is now unlocked!", o.name).into(), - "".into(), - ) - .submit(app); - } else { - Toast::new( - ToastTopic::System, - format!("{} is now locked in place!", o.name).into(), - "".into(), - ) - .submit(app); - } - }), - )); - audio_thump(app); - } - OverlayAction::ToggleInteraction => { - app.tasks.enqueue(TaskType::Overlay( - overlay.clone(), - Box::new(|app, o| { - o.interactable = !o.interactable; - if o.interactable { - Toast::new( - ToastTopic::System, - format!("{} is now interactable!", o.name).into(), - "".into(), - ) - .submit(app); - } else { - Toast::new( - ToastTopic::System, - format!("{} is now non-interactable!", o.name).into(), - "".into(), - ) - .submit(app); - } - }), - )); - audio_thump(app); - } - OverlayAction::Opacity { delta } => { - let delta = *delta; - app.tasks.enqueue(TaskType::Overlay( - overlay.clone(), - Box::new(move |_, o| { - o.alpha = (o.alpha + delta).clamp(0.1, 1.0); - o.dirty = true; - log::debug!("{}: alpha {}", o.name, o.alpha); - }), - )); - } - } -} - -fn run_window(window: &Arc, action: &WindowAction, app: &mut AppState) { - use crate::overlays::custom; - - match action { - WindowAction::ShowMirror => { - #[cfg(feature = "wayland")] - app.tasks.enqueue(TaskType::CreateOverlay( - OverlaySelector::Name(window.clone()), - Box::new({ - let name = window.clone(); - move |app| { - Toast::new( - ToastTopic::System, - "Check your desktop for popup.".into(), - "".into(), - ) - .with_sound(true) - .submit(app); - Some(crate::overlays::mirror::new_mirror( - name, - false, - &app.session, - )) - } - }), - )); - #[cfg(not(feature = "wayland"))] - log::warn!("Mirror not available without Wayland feature."); - } - WindowAction::ShowUi => { - app.tasks.enqueue(TaskType::CreateOverlay( - OverlaySelector::Name(window.clone()), - Box::new({ - let name = window.clone(); - move |app| custom::create_custom(app, name) - }), - )); - } - WindowAction::Destroy => { - app.tasks - .enqueue(TaskType::DropOverlay(OverlaySelector::Name(window.clone()))); - } - } -} - -const THUMP_AUDIO_WAV: &[u8] = include_bytes!("../../res/380885.wav"); - -fn audio_thump(app: &mut AppState) { - app.audio.play(THUMP_AUDIO_WAV); -} diff --git a/src/gui/modular/label.rs b/src/gui/modular/label.rs deleted file mode 100644 index 98626ea..0000000 --- a/src/gui/modular/label.rs +++ /dev/null @@ -1,305 +0,0 @@ -use chrono::Local; -use chrono_tz::Tz; -use glam::Vec4; -use smallvec::SmallVec; -use std::{ - io::Read, - process::{self, Stdio}, - sync::Arc, - time::Instant, -}; - -use crate::{ - gui::modular::FALLBACK_COLOR, - overlays::toast::{error_toast, error_toast_str}, - state::AppState, -}; - -use serde::Deserialize; - -use super::{color_parse_or_default, ExecArgs, GuiColor, ModularControl, ModularData}; - -#[derive(Deserialize)] -#[serde(untagged)] -pub enum TimezoneDef { - Idx(usize), - Str(Arc), -} - -#[derive(Deserialize)] -#[serde(tag = "source")] -pub enum LabelContent { - Static { - text: Arc, - }, - Exec { - command: ExecArgs, - interval: f32, - }, - Clock { - format: Arc, - timezone: Option, - }, - Timezone { - timezone: usize, - }, - Timer { - format: Arc, - }, - Battery { - device: usize, - low_threshold: f32, - low_color: Arc, - charging_color: Arc, - }, - DragMultiplier, - Ipd, -} - -pub enum LabelData { - Battery { - device: usize, - low_threshold: f32, - normal_color: GuiColor, - low_color: GuiColor, - charging_color: GuiColor, - }, - Clock { - format: Arc, - timezone: Option, - }, - Timer { - format: Arc, - start: Instant, - }, - Exec { - last_exec: Instant, - interval: f32, - command: Vec>, - child: Option, - }, - Ipd { - last_ipd: f32, - }, - DragMultiplier, -} - -pub fn modular_label_init(label: &mut ModularControl, content: &LabelContent, app: &AppState) { - let state = match content { - LabelContent::Battery { - device, - low_threshold, - low_color, - charging_color, - } => Some(LabelData::Battery { - device: *device, - low_threshold: *low_threshold, - normal_color: label.fg_color, - low_color: color_parse_or_default(low_color), - charging_color: color_parse_or_default(charging_color), - }), - LabelContent::Clock { format, timezone } => { - let tz_str = match timezone { - Some(TimezoneDef::Idx(idx)) => app.session.config.timezones.get(*idx).map_or_else( - || { - log::error!("Timezone index out of range '{idx}'"); - label.set_fg_color(*FALLBACK_COLOR); - None - }, - |tz| Some(tz.as_str()), - ), - Some(TimezoneDef::Str(tz_str)) => Some(tz_str.as_ref()), - None => None, - }; - - Some(LabelData::Clock { - format: format.clone(), - timezone: tz_str.and_then(|tz| { - tz.parse() - .map_err(|_| { - log::error!("Failed to parse timezone '{}'", &tz); - label.set_fg_color(*FALLBACK_COLOR); - }) - .ok() - }), - }) - } - LabelContent::Timezone { timezone } => { - if let Some(tz) = app.session.config.timezones.get(*timezone) { - let pretty_tz = tz.split('/').next_back().map(|x| x.replace('_', " ")); - - if let Some(pretty_tz) = pretty_tz { - label.set_text(&pretty_tz); - return; - } - log::error!("Timezone name not valid '{}'", &tz); - } else { - log::error!("Timezone index out of range '{}'", &timezone); - } - label.set_fg_color(*FALLBACK_COLOR); - label.set_text("Error"); - None - } - LabelContent::Timer { format } => Some(LabelData::Timer { - format: format.clone(), - start: Instant::now(), - }), - LabelContent::Exec { command, interval } => Some(LabelData::Exec { - last_exec: Instant::now(), - interval: *interval, - command: command.clone(), - child: None, - }), - LabelContent::Static { text } => { - label.set_text(text); - None - } - LabelContent::Ipd => Some(LabelData::Ipd { last_ipd: -1. }), - LabelContent::DragMultiplier => Some(LabelData::DragMultiplier), - }; - - if let Some(state) = state { - label.state = Some(ModularData::Label(Box::new(state))); - label.on_update = Some(label_update); - } -} - -#[allow(clippy::too_many_lines)] -pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut AppState) { - // want panic - let ModularData::Label(data) = control.state.as_mut().unwrap() else { - panic!("Label control has no state"); - }; - match data.as_mut() { - LabelData::Battery { - device, - low_threshold, - normal_color, - low_color, - charging_color, - } => { - let device = app.input_state.devices.get(*device); - - let tags = ["", "H", "L", "R", "T"]; - - if let Some(device) = device { - let (text, color) = device.soc.map_or_else( - || (String::new(), Vec4::ZERO), - |soc| { - let text = format!( - "{}{}", - tags[device.role as usize], - (soc * 100.).min(99.) as u32 - ); - let color = if device.charging { - *charging_color - } else if soc < *low_threshold { - *low_color - } else { - *normal_color - }; - (text, color) - }, - ); - - control.set_text(&text); - control.set_fg_color(color); - } else { - control.set_text(""); - } - } - LabelData::Clock { format, timezone } => { - let format = format.clone(); - if let Some(tz) = timezone { - let date_time = Local::now().with_timezone(tz); - control.set_text(&format!("{}", &date_time.format(&format))); - } else { - let date_time = Local::now(); - control.set_text(&format!("{}", &date_time.format(&format))); - } - } - LabelData::Timer { format, start } => { - let mut format = format.clone().to_lowercase(); - let duration = start.elapsed().as_secs(); - format = format.replace("%s", &format!("{:02}", (duration % 60))); - format = format.replace("%m", &format!("{:02}", ((duration / 60) % 60))); - format = format.replace("%h", &format!("{:02}", ((duration / 60) / 60))); - control.set_text(&format); - } - LabelData::Exec { - last_exec, - interval, - command, - child, - } => { - if let Some(mut proc) = child.take() { - match proc.try_wait() { - Ok(Some(code)) => { - if code.success() { - if let Some(mut stdout) = proc.stdout.take() { - let mut buf = String::new(); - if stdout.read_to_string(&mut buf).is_ok() { - control.set_text(&buf); - } else { - error_toast_str( - app, - "LabelData::Exec: Failed to read stdout for child process", - ); - return; - } - return; - } - log::error!("No stdout for child process"); - return; - } - error_toast(app, "LabelData::Exec: Child process exited with code", code); - } - Ok(None) => { - *child = Some(proc); - // not exited yet - return; - } - Err(e) => { - *child = None; - error_toast(app, "Error checking child process", e); - return; - } - } - } - - if Instant::now() - .saturating_duration_since(*last_exec) - .as_secs_f32() - > *interval - { - *last_exec = Instant::now(); - let args = command - .iter() - .map(std::convert::AsRef::as_ref) - .collect::>(); - - match process::Command::new(args[0]) - .args(&args[1..]) - .stdout(Stdio::piped()) - .spawn() - { - Ok(proc) => { - *child = Some(proc); - } - Err(e) => { - error_toast(app, &format!("Failed to spawn process {args:?}"), e); - } - } - } - } - LabelData::Ipd { last_ipd } => { - if (app.input_state.ipd - *last_ipd).abs() > 0.05 { - *last_ipd = app.input_state.ipd; - control.set_text(&format!("{:.1}", app.input_state.ipd)); - } - } - LabelData::DragMultiplier => { - control.set_text(&format!("{:.1}", app.session.config.space_drag_multiplier)); - } - } -} diff --git a/src/gui/modular/mod.rs b/src/gui/modular/mod.rs deleted file mode 100644 index 3763580..0000000 --- a/src/gui/modular/mod.rs +++ /dev/null @@ -1,569 +0,0 @@ -pub mod button; -pub mod label; - -use std::{fs::File, sync::Arc}; - -#[cfg(feature = "wayvr")] -use button::{WayVRAction, WayVRDisplayClickAction}; - -use glam::Vec4; -use serde::Deserialize; -use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView}; - -use crate::{ - backend::common::OverlaySelector, config::AStrMapExt, config_io, - graphics::dds::WlxCommandBufferDds, state::AppState, -}; - -use self::{ - button::{modular_button_init, ButtonAction, ButtonData, OverlayAction}, - label::{modular_label_init, LabelContent, LabelData}, -}; - -use super::{ - canvas::{builder::CanvasBuilder, control::Control, Canvas}, - color_parse, GuiColor, FALLBACK_COLOR, -}; - -type ModularControl = Control<(), ModularData>; -type ExecArgs = Vec>; - -#[derive(Deserialize)] -pub struct ModularUiConfig { - pub width: f32, - pub size: [u32; 2], - pub spawn_pos: Option<[f32; 3]>, - pub elements: Vec, -} - -#[derive(Deserialize)] -pub struct OverlayListTemplate { - click_down: Option, - click_up: Option, - long_click_up: Option, - right_down: Option, - right_up: Option, - long_right_up: Option, - middle_down: Option, - middle_up: Option, - long_middle_up: Option, - scroll_down: Option, - scroll_up: Option, -} - -#[allow(dead_code)] -#[derive(Deserialize)] -#[serde(tag = "type")] -pub enum ModularElement { - Panel { - rect: [f32; 4], - corner_radius: Option, - bg_color: Arc, - }, - Label { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - #[serde(flatten)] - data: LabelContent, - }, - CenteredLabel { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - #[serde(flatten)] - data: LabelContent, - }, - Sprite { - rect: [f32; 4], - sprite: Arc, - sprite_st: Option<[f32; 4]>, - }, - Button { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - bg_color: Arc, - text: Arc, - #[serde(flatten)] - data: Box, - }, - /// Convenience type to save you from having to create a bunch of labels - BatteryList { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - fg_color_low: Arc, - fg_color_charging: Arc, - low_threshold: f32, - num_devices: usize, - layout: ListLayout, - }, - OverlayList { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - bg_color: Arc, - layout: ListLayout, - #[serde(flatten)] - template: Box, - }, - // Ignored if "wayvr" feature is not enabled - WayVRLauncher { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - bg_color: Arc, - catalog_name: Arc, - }, - // Ignored if "wayvr" feature is not enabled - WayVRDisplayList { - rect: [f32; 4], - corner_radius: Option, - font_size: isize, - fg_color: Arc, - bg_color: Arc, - }, -} - -#[derive(Deserialize, Clone)] -pub enum ButtonFunc { - HideWatch, - SwitchWatchHand, -} - -#[derive(Deserialize)] -pub enum ListLayout { - Horizontal, - Vertical, -} - -pub enum ModularData { - Label(Box), - Button(Box), -} - -#[allow(clippy::too_many_lines, clippy::many_single_char_names)] -pub fn modular_canvas( - size: [u32; 2], - elements: &[ModularElement], - state: &mut AppState, -) -> anyhow::Result> { - let mut canvas = CanvasBuilder::new( - size[0] as _, - size[1] as _, - state.graphics.clone(), - state.graphics.native_format, - (), - )?; - let empty_str: Arc = Arc::from(""); - for elem in elements { - match elem { - ModularElement::Panel { - rect: [x, y, w, h], - corner_radius, - bg_color, - } => { - canvas.bg_color = color_parse(bg_color).unwrap_or(*FALLBACK_COLOR); - canvas.panel(*x, *y, *w, *h, corner_radius.unwrap_or_default()); - } - ModularElement::Label { - rect: [x, y, w, h], - corner_radius, - font_size, - fg_color, - data, - } => { - canvas.font_size = *font_size; - canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - let label = canvas.label( - *x, - *y, - *w, - *h, - corner_radius.unwrap_or_default(), - empty_str.clone(), - ); - modular_label_init(label, data, state); - } - ModularElement::CenteredLabel { - rect: [x, y, w, h], - corner_radius, - font_size, - fg_color, - data, - } => { - canvas.font_size = *font_size; - canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - let label = canvas.label_centered( - *x, - *y, - *w, - *h, - corner_radius.unwrap_or_default(), - empty_str.clone(), - ); - modular_label_init(label, data, state); - } - ModularElement::Sprite { - rect: [x, y, w, h], - sprite, - sprite_st, - } => match sprite_from_path(sprite.clone(), state) { - Ok(view) => { - let sprite = canvas.sprite(*x, *y, *w, *h); - sprite.fg_color = Vec4::ONE; - sprite.set_sprite(view); - - let st = sprite_st - .map(|st| Vec4::from_slice(&st)) - .unwrap_or_else(|| Vec4::new(1., 1., 0., 0.)); - sprite.set_sprite_st(st); - } - Err(e) => { - log::warn!("Could not load custom UI sprite: {e:?}"); - } - }, - ModularElement::Button { - rect: [x, y, w, h], - corner_radius, - font_size, - bg_color, - fg_color, - text, - data, - } => { - canvas.bg_color = color_parse(bg_color).unwrap_or(*FALLBACK_COLOR); - canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - canvas.font_size = *font_size; - let button = canvas.button( - *x, - *y, - *w, - *h, - corner_radius.unwrap_or_default(), - text.clone(), - ); - modular_button_init(button, data); - } - ModularElement::BatteryList { - rect: [x, y, w, h], - corner_radius, - font_size, - fg_color, - fg_color_low, - fg_color_charging, - low_threshold, - num_devices, - layout, - } => { - let num_buttons = *num_devices as f32; - let mut button_x = *x; - let mut button_y = *y; - let low_threshold = low_threshold * 0.01; - let (button_w, button_h) = match layout { - ListLayout::Horizontal => (*w / num_buttons, *h), - ListLayout::Vertical => (*w, *h / num_buttons), - }; - - let fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - canvas.font_size = *font_size; - canvas.fg_color = fg_color; - - for i in 0..*num_devices { - let label = canvas.label_centered( - button_x + 2., - button_y + 2., - button_w - 4., - button_h - 4., - corner_radius.unwrap_or_default(), - empty_str.clone(), - ); - modular_label_init( - label, - &LabelContent::Battery { - device: i, - low_threshold, - low_color: fg_color_low.clone(), - charging_color: fg_color_charging.clone(), - }, - state, - ); - - button_x += match layout { - ListLayout::Horizontal => button_w, - ListLayout::Vertical => 0., - }; - button_y += match layout { - ListLayout::Horizontal => 0., - ListLayout::Vertical => button_h, - }; - } - } - ModularElement::OverlayList { - rect: [x, y, w, h], - corner_radius, - font_size, - fg_color, - bg_color, - layout, - template, - } => { - let num_buttons = state.screens.len() as f32; - let mut button_x = *x; - let mut button_y = *y; - let (button_w, button_h) = match layout { - ListLayout::Horizontal => (*w / num_buttons, *h), - ListLayout::Vertical => (*w, *h / num_buttons), - }; - - canvas.bg_color = color_parse(bg_color).unwrap_or(*FALLBACK_COLOR); - canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - canvas.font_size = *font_size; - - for screen in &state.screens { - let button = canvas.button( - button_x + 2., - button_y + 2., - button_w - 4., - button_h - 4., - corner_radius.unwrap_or_default(), - screen.name.clone(), - ); - - // cursed - let data = ButtonData { - click_down: template.click_down.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - click_up: template.click_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - long_click_up: template.long_click_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - right_down: template.right_down.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - right_up: template.right_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - long_right_up: template.long_right_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - middle_down: template.middle_down.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - middle_up: template.middle_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - long_middle_up: template.long_middle_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - scroll_down: template.scroll_down.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - scroll_up: template.scroll_up.as_ref().map(|f| { - vec![ButtonAction::Overlay { - target: OverlaySelector::Id(screen.id), - action: f.clone(), - }] - }), - ..Default::default() - }; - - modular_button_init(button, &data); - - button_x += match layout { - ListLayout::Horizontal => button_w, - ListLayout::Vertical => 0., - }; - button_y += match layout { - ListLayout::Horizontal => 0., - ListLayout::Vertical => button_h, - }; - } - } - #[allow(unused_variables)] // needed in case if wayvr feature is not enabled - ModularElement::WayVRLauncher { - rect: [x, y, w, h], - corner_radius, - font_size, - fg_color, - bg_color, - catalog_name, - } => { - #[cfg(feature = "wayvr")] - { - if let Some(catalog) = state.session.wayvr_config.get_catalog(catalog_name) { - let mut button_x = *x; - let button_y = *y; - - for app in &catalog.apps { - let button_w: f32 = *w / catalog.apps.len() as f32; - let button_h: f32 = *h; - - canvas.bg_color = color_parse(bg_color).unwrap_or(*FALLBACK_COLOR); - canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - canvas.font_size = *font_size; - - let button = canvas.button( - button_x + 2., - button_y + 2., - button_w - 4., - button_h - 4., - corner_radius.unwrap_or_default(), - Arc::from(app.name.as_str()), - ); - - let data = ButtonData { - click_up: Some(vec![ButtonAction::WayVR { - action: WayVRAction::AppClick { - catalog_name: catalog_name.clone(), - app_name: Arc::from(app.name.as_str()), - }, - }]), - ..Default::default() - }; - - modular_button_init(button, &data); - button_x += button_w; - } - } else { - log::error!("WayVR catalog \"{catalog_name}\" not found"); - } - } - #[cfg(not(feature = "wayvr"))] - { - log::error!("WayVR feature is not enabled, ignoring"); - } - } - #[allow(unused_variables)] - ModularElement::WayVRDisplayList { - rect: [x, y, w, h], - corner_radius, - font_size, - fg_color, - bg_color, - } => { - #[cfg(feature = "wayvr")] - { - let mut button_x = *x; - let button_y = *y; - let displays = &state.session.wayvr_config.displays; - for (display_name, display) in displays { - let button_w: f32 = (*w / displays.len() as f32).min(80.0); - let button_h: f32 = *h; - - canvas.bg_color = color_parse(bg_color).unwrap_or(*FALLBACK_COLOR); - canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); - canvas.font_size = *font_size; - - let button = canvas.button( - button_x + 2., - button_y + 2., - button_w - 4., - button_h - 4., - corner_radius.unwrap_or_default(), - Arc::from(display_name.as_str()), - ); - - let data = ButtonData { - click_up: Some(vec![ButtonAction::WayVR { - action: WayVRAction::DisplayClick { - display_name: Arc::from(display_name.as_str()), - action: WayVRDisplayClickAction::ToggleVisibility, - }, - }]), - long_click_up: Some(vec![ButtonAction::WayVR { - action: WayVRAction::DisplayClick { - display_name: Arc::from(display_name.as_str()), - action: WayVRDisplayClickAction::Reset, - }, - }]), - ..Default::default() - }; - - modular_button_init(button, &data); - button_x += button_w; - } - } - #[cfg(not(feature = "wayvr"))] - { - log::error!("WayVR feature is not enabled, ignoring") - } - } - } - } - Ok(canvas.build()) -} - -pub fn color_parse_or_default(color: &str) -> GuiColor { - color_parse(color).unwrap_or_else(|e| { - log::error!("Failed to parse color '{color}': {e}"); - *FALLBACK_COLOR - }) -} - -fn sprite_from_path(path: Arc, app: &mut AppState) -> anyhow::Result> { - if let Some(view) = app.sprites.arc_get(&path) { - return Ok(view.clone()); - } - - let real_path = config_io::get_config_root().join(&*path); - - let Ok(f) = File::open(real_path) else { - anyhow::bail!("Could not open custom sprite at: {}", path); - }; - - let mut command_buffer = app.graphics.create_uploads_command_buffer( - app.graphics.transfer_queue.clone(), - CommandBufferUsage::OneTimeSubmit, - )?; - - match command_buffer.texture2d_dds(f) { - Ok(image) => { - command_buffer.build_and_execute_now()?; - Ok(ImageView::new_default(image)?) - } - Err(e) => { - anyhow::bail!("Could not use custom sprite at: {}\n{:?}", path, e); - } - } -} diff --git a/src/overlays/anchor.rs b/src/overlays/anchor.rs deleted file mode 100644 index a473763..0000000 --- a/src/overlays/anchor.rs +++ /dev/null @@ -1,32 +0,0 @@ -use glam::Vec3A; -use std::sync::{Arc, LazyLock}; - -use crate::backend::overlay::{OverlayData, OverlayState, Positioning, Z_ORDER_ANCHOR}; -use crate::config::{load_known_yaml, ConfigType}; -use crate::gui::modular::{modular_canvas, ModularUiConfig}; -use crate::state::AppState; - -pub static ANCHOR_NAME: LazyLock> = LazyLock::new(|| Arc::from("anchor")); - -pub fn create_anchor(state: &mut AppState) -> anyhow::Result> -where - O: Default, -{ - let config = load_known_yaml::(ConfigType::Anchor); - - Ok(OverlayData { - state: OverlayState { - name: ANCHOR_NAME.clone(), - want_visible: false, - interactable: false, - grabbable: false, - z_order: Z_ORDER_ANCHOR, - spawn_scale: config.width, - spawn_point: Vec3A::NEG_Z * 0.5, - positioning: Positioning::Static, - ..Default::default() - }, - backend: Box::new(modular_canvas(config.size, &config.elements, state)?), - ..Default::default() - }) -} diff --git a/src/overlays/custom.rs b/src/overlays/custom.rs deleted file mode 100644 index 5bf8a54..0000000 --- a/src/overlays/custom.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::sync::Arc; - -use glam::Vec3A; - -use crate::{ - backend::overlay::{ui_transform, OverlayBackend, OverlayState}, - config::{load_custom_ui, load_known_yaml, ConfigType}, - gui::modular::{modular_canvas, ModularUiConfig}, - state::AppState, -}; - -const SETTINGS_NAME: &str = "settings"; - -pub fn create_custom( - state: &mut AppState, - name: Arc, -) -> Option<(OverlayState, Box)> { - let config = if &*name == SETTINGS_NAME { - load_known_yaml::(ConfigType::Settings) - } else { - match load_custom_ui(&name) { - Ok(config) => config, - Err(e) => { - log::error!("Failed to load custom UI config for {name}: {e:?}"); - return None; - } - } - }; - - let canvas = match modular_canvas(config.size, &config.elements, state) { - Ok(canvas) => canvas, - Err(e) => { - log::error!("Failed to create canvas for {name}: {e:?}"); - return None; - } - }; - - let state = OverlayState { - name, - want_visible: true, - interactable: true, - grabbable: true, - spawn_scale: config.width, - spawn_point: Vec3A::from_array(config.spawn_pos.unwrap_or([0., 0., -0.5])), - interaction_transform: ui_transform(config.size), - ..Default::default() - }; - let backend = Box::new(canvas); - - Some((state, backend)) -} diff --git a/src/overlays/keyboard.rs b/src/overlays/keyboard.rs deleted file mode 100644 index aa32415..0000000 --- a/src/overlays/keyboard.rs +++ /dev/null @@ -1,573 +0,0 @@ -use std::{ - collections::HashMap, - process::{Child, Command}, - str::FromStr, - sync::{Arc, LazyLock}, -}; - -use crate::{ - backend::{ - input::{InteractionHandler, PointerMode}, - overlay::{ - FrameMeta, OverlayBackend, OverlayData, OverlayRenderer, OverlayState, Positioning, - ShouldRender, - }, - }, - config::{self, ConfigType}, - graphics::CommandBuffers, - gui::{ - canvas::{builder::CanvasBuilder, control::Control, Canvas}, - color_parse, KeyCapType, - }, - hid::{ - get_key_type, KeyModifier, KeyType, VirtualKey, XkbKeymap, ALT, CTRL, KEYS_TO_MODS, META, - NUM_LOCK, SHIFT, SUPER, - }, - state::{AppState, KeyboardFocus}, -}; -use glam::{vec2, vec3a, Affine2, Vec4}; -use regex::Regex; -use serde::{Deserialize, Serialize}; -use vulkano::image::view::ImageView; - -const PIXELS_PER_UNIT: f32 = 80.; -const BUTTON_PADDING: f32 = 4.; -const AUTO_RELEASE_MODS: [KeyModifier; 5] = [SHIFT, CTRL, ALT, SUPER, META]; - -pub const KEYBOARD_NAME: &str = "kbd"; - -fn send_key(app: &mut AppState, key: VirtualKey, down: bool) { - match app.keyboard_focus { - KeyboardFocus::PhysicalScreen => { - app.hid_provider.send_key(key, down); - } - KeyboardFocus::WayVR => - { - #[cfg(feature = "wayvr")] - if let Some(wayvr) = &app.wayvr { - wayvr.borrow_mut().data.state.send_key(key as u32, down); - } - } - } -} - -fn set_modifiers(app: &mut AppState, mods: u8) { - match app.keyboard_focus { - KeyboardFocus::PhysicalScreen => { - app.hid_provider.set_modifiers(mods); - } - KeyboardFocus::WayVR => - { - #[cfg(feature = "wayvr")] - if let Some(wayvr) = &app.wayvr { - wayvr.borrow_mut().data.state.set_modifiers(mods); - } - } - } -} - -#[allow(clippy::too_many_lines)] -pub fn create_keyboard( - app: &AppState, - mut keymap: Option, -) -> anyhow::Result> -where - O: Default, -{ - let size = vec2( - LAYOUT.row_size * PIXELS_PER_UNIT, - (LAYOUT.main_layout.len() as f32) * PIXELS_PER_UNIT, - ); - - let data = KeyboardData { - modifiers: 0, - alt_modifier: match LAYOUT.alt_modifier { - AltModifier::Shift => SHIFT, - AltModifier::Ctrl => CTRL, - AltModifier::Alt => ALT, - AltModifier::Super => SUPER, - AltModifier::Meta => META, - _ => 0, - }, - processes: vec![], - }; - - let mut canvas = CanvasBuilder::new( - size.x as _, - size.y as _, - app.graphics.clone(), - app.graphics.native_format, - data, - )?; - - canvas.bg_color = color_parse("#181926").unwrap(); //safe - canvas.panel(0., 0., size.x, size.y, 12.); - - canvas.font_size = 18; - canvas.fg_color = color_parse("#cad3f5").unwrap(); //safe - canvas.bg_color = color_parse("#1e2030").unwrap(); //safe - - let has_altgr = keymap - .as_ref() - .is_some_and(super::super::hid::XkbKeymap::has_altgr); - - if !LAYOUT.auto_labels.unwrap_or(true) { - keymap = None; - } - - let unit_size = size.x / LAYOUT.row_size; - let h = 2.0f32.mul_add(-BUTTON_PADDING, unit_size); - - for row in 0..LAYOUT.key_sizes.len() { - let y = unit_size.mul_add(row as f32, BUTTON_PADDING); - let mut sum_size = 0f32; - - for col in 0..LAYOUT.key_sizes[row].len() { - let my_size = LAYOUT.key_sizes[row][col]; - let x = unit_size.mul_add(sum_size, BUTTON_PADDING); - let w = unit_size.mul_add(my_size, -(2. * BUTTON_PADDING)); - - if let Some(key) = LAYOUT.main_layout[row][col].as_ref() { - let mut label = Vec::with_capacity(2); - let mut maybe_state: Option = None; - let mut cap_type = KeyCapType::Regular; - - if let Ok(vk) = VirtualKey::from_str(key) { - if let Some(keymap) = keymap.as_ref() { - match get_key_type(vk) { - KeyType::Symbol => { - let label0 = keymap.label_for_key(vk, 0); - let label1 = keymap.label_for_key(vk, SHIFT); - - if label0.chars().next().is_some_and(char::is_alphabetic) { - label.push(label1); - if has_altgr { - cap_type = KeyCapType::RegularAltGr; - label.push(keymap.label_for_key(vk, META)); - } else { - cap_type = KeyCapType::Regular; - } - } else { - label.push(label0); - label.push(label1); - if has_altgr { - label.push(keymap.label_for_key(vk, META)); - cap_type = KeyCapType::ReversedAltGr; - } else { - cap_type = KeyCapType::Reversed; - } - } - } - KeyType::NumPad => { - label.push(keymap.label_for_key(vk, NUM_LOCK)); - } - KeyType::Other => {} - } - } - - if let Some(mods) = KEYS_TO_MODS.get(vk) { - maybe_state = Some(KeyButtonData::Modifier { - modifier: *mods, - sticky: false, - }); - } else { - maybe_state = Some(KeyButtonData::Key { vk, pressed: false }); - } - } else if let Some(macro_verbs) = LAYOUT.macros.get(key) { - maybe_state = Some(KeyButtonData::Macro { - verbs: key_events_for_macro(macro_verbs), - }); - } else if let Some(exec_args) = LAYOUT.exec_commands.get(key) { - if exec_args.is_empty() { - log::error!("Keyboard: EXEC args empty for {key}"); - continue; - } - let mut iter = exec_args.iter().cloned(); - if let Some(program) = iter.next() { - maybe_state = Some(KeyButtonData::Exec { - program, - args: iter.by_ref().take_while(|arg| arg[..] != *"null").collect(), - release_program: iter.next(), - release_args: iter.collect(), - }); - } - } else { - log::error!("Unknown key: {key}"); - } - - if let Some(state) = maybe_state { - if label.is_empty() { - label = LAYOUT.label_for_key(key); - } - let button = canvas.key_button(x, y, w, h, 12., cap_type, &label); - button.state = Some(state); - button.on_press = Some(key_press); - button.on_release = Some(key_release); - button.test_highlight = Some(test_highlight); - } - } - - sum_size += my_size; - } - } - - let canvas = canvas.build(); - - let interaction_transform = Affine2::from_translation(vec2(0.5, 0.5)) - * Affine2::from_scale(vec2(1., -size.x as f32 / size.y as f32)); - - let width = LAYOUT.row_size * 0.05 * app.session.config.keyboard_scale; - - Ok(OverlayData { - state: OverlayState { - name: KEYBOARD_NAME.into(), - grabbable: true, - recenter: true, - positioning: Positioning::Anchored, - interactable: true, - spawn_scale: width, - spawn_point: vec3a(0., -0.5, 0.), - interaction_transform, - ..Default::default() - }, - backend: Box::new(KeyboardBackend { canvas }), - ..Default::default() - }) -} - -fn key_press( - control: &mut Control, - data: &mut KeyboardData, - app: &mut AppState, - mode: PointerMode, -) { - match control.state.as_mut() { - Some(KeyButtonData::Key { vk, pressed }) => { - key_click(app); - - data.modifiers |= match mode { - PointerMode::Right => SHIFT, - PointerMode::Middle => data.alt_modifier, - _ => 0, - }; - - set_modifiers(app, data.modifiers); - - send_key(app, *vk, true); - *pressed = true; - } - Some(KeyButtonData::Modifier { modifier, sticky }) => { - *sticky = data.modifiers & *modifier == 0; - data.modifiers |= *modifier; - key_click(app); - set_modifiers(app, data.modifiers); - } - Some(KeyButtonData::Macro { verbs }) => { - key_click(app); - for (vk, press) in verbs { - send_key(app, *vk, *press); - } - } - Some(KeyButtonData::Exec { program, args, .. }) => { - // Reap previous processes - data.processes - .retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_)))); - - key_click(app); - if let Ok(child) = Command::new(program).args(args).spawn() { - data.processes.push(child); - } - } - None => {} - } -} - -fn key_release( - control: &mut Control, - data: &mut KeyboardData, - app: &mut AppState, -) { - match control.state.as_mut() { - Some(KeyButtonData::Key { vk, pressed }) => { - send_key(app, *vk, false); - *pressed = false; - - for m in &AUTO_RELEASE_MODS { - if data.modifiers & *m != 0 { - data.modifiers &= !*m; - set_modifiers(app, data.modifiers); - } - } - } - Some(KeyButtonData::Modifier { modifier, sticky }) => { - if !*sticky { - data.modifiers &= !*modifier; - set_modifiers(app, data.modifiers); - } - } - Some(KeyButtonData::Exec { - release_program, - release_args, - .. - }) => { - // Reap previous processes - data.processes - .retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_)))); - - if let Some(program) = release_program { - if let Ok(child) = Command::new(program).args(release_args).spawn() { - data.processes.push(child); - } - } - } - _ => {} - } -} - -static PRESS_COLOR: Vec4 = Vec4::new(198. / 255., 160. / 255., 246. / 255., 0.5); - -fn test_highlight( - control: &Control, - data: &mut KeyboardData, - _app: &mut AppState, -) -> Option { - let pressed = match control.state.as_ref() { - Some(KeyButtonData::Key { pressed, .. }) => *pressed, - Some(KeyButtonData::Modifier { modifier, .. }) => data.modifiers & *modifier != 0, - _ => false, - }; - - if pressed { - Some(PRESS_COLOR) - } else { - None - } -} - -struct KeyboardData { - modifiers: KeyModifier, - alt_modifier: KeyModifier, - processes: Vec, -} - -const KEY_AUDIO_WAV: &[u8] = include_bytes!("../res/421581.wav"); - -fn key_click(app: &mut AppState) { - if app.session.config.keyboard_sound_enabled { - app.audio.play(KEY_AUDIO_WAV); - } -} - -enum KeyButtonData { - Key { - vk: VirtualKey, - pressed: bool, - }, - Modifier { - modifier: KeyModifier, - sticky: bool, - }, - Macro { - verbs: Vec<(VirtualKey, bool)>, - }, - Exec { - program: String, - args: Vec, - release_program: Option, - release_args: Vec, - }, -} - -static LAYOUT: LazyLock = LazyLock::new(Layout::load_from_disk); - -static MACRO_REGEX: LazyLock = - LazyLock::new(|| Regex::new(r"^([A-Za-z0-9_-]+)(?: +(UP|DOWN))?$").unwrap()); // want panic - -#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize)] -#[repr(usize)] -pub enum AltModifier { - #[default] - None, - Shift, - Ctrl, - Alt, - Super, - Meta, -} - -#[derive(Debug, Deserialize, Serialize)] -#[allow(clippy::struct_field_names)] -pub struct Layout { - name: String, - row_size: f32, - key_sizes: Vec>, - main_layout: Vec>>, - alt_modifier: AltModifier, - exec_commands: HashMap>, - macros: HashMap>, - labels: HashMap>, - auto_labels: Option, -} - -impl Layout { - fn load_from_disk() -> Self { - let mut layout = config::load_known_yaml::(ConfigType::Keyboard); - layout.post_load(); - layout - } - - fn post_load(&mut self) { - for i in 0..self.key_sizes.len() { - let row = &self.key_sizes[i]; - let width: f32 = row.iter().sum(); - assert!( - (width - self.row_size).abs() < 0.001, - "Row {} has a width of {}, but the row size is {}", - i, - width, - self.row_size - ); - } - - for i in 0..self.main_layout.len() { - let row = &self.main_layout[i]; - let width = row.len(); - assert!( - (width == self.key_sizes[i].len()), - "Row {} has {} keys, needs to have {} according to key_sizes", - i, - width, - self.key_sizes[i].len() - ); - } - } - - fn label_for_key(&self, key: &str) -> Vec { - if let Some(label) = self.labels.get(key) { - return label.clone(); - } - if key.is_empty() { - return vec![]; - } - if key.len() == 1 { - return vec![key.to_string().to_lowercase()]; - } - let mut key = key; - if key.starts_with("KP_") { - key = &key[3..]; - } - if key.contains('_') { - key = key.split('_').next().unwrap_or_else(|| { - log::error!("keyboard.yaml: Key '{key}' must not start or end with '_'!"); - "???" - }); - } - vec![format!( - "{}{}", - key.chars().next().unwrap().to_uppercase(), // safe because we checked is_empty - &key[1..].to_lowercase() - )] - } -} - -fn key_events_for_macro(macro_verbs: &Vec) -> Vec<(VirtualKey, bool)> { - let mut key_events = vec![]; - for verb in macro_verbs { - if let Some(caps) = MACRO_REGEX.captures(verb) { - if let Ok(virtual_key) = VirtualKey::from_str(&caps[1]) { - if let Some(state) = caps.get(2) { - if state.as_str() == "UP" { - key_events.push((virtual_key, false)); - } else if state.as_str() == "DOWN" { - key_events.push((virtual_key, true)); - } else { - log::error!( - "Unknown key state in macro: {}, looking for UP or DOWN.", - state.as_str() - ); - return vec![]; - } - } else { - key_events.push((virtual_key, true)); - key_events.push((virtual_key, false)); - } - } else { - log::error!("Unknown virtual key: {}", &caps[1]); - return vec![]; - } - } - } - key_events -} - -struct KeyboardBackend { - canvas: Canvas, -} - -impl OverlayBackend for KeyboardBackend { - fn set_interaction(&mut self, interaction: Box) { - self.canvas.set_interaction(interaction); - } - fn set_renderer(&mut self, renderer: Box) { - self.canvas.set_renderer(renderer); - } -} - -impl InteractionHandler for KeyboardBackend { - fn on_pointer( - &mut self, - app: &mut AppState, - hit: &crate::backend::input::PointerHit, - pressed: bool, - ) { - self.canvas.on_pointer(app, hit, pressed); - } - fn on_scroll( - &mut self, - app: &mut AppState, - hit: &crate::backend::input::PointerHit, - delta_y: f32, - delta_x: f32, - ) { - self.canvas.on_scroll(app, hit, delta_y, delta_x); - } - fn on_left(&mut self, app: &mut AppState, pointer: usize) { - self.canvas.on_left(app, pointer); - } - fn on_hover( - &mut self, - app: &mut AppState, - hit: &crate::backend::input::PointerHit, - ) -> Option { - self.canvas.on_hover(app, hit) - } -} - -impl OverlayRenderer for KeyboardBackend { - fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> { - self.canvas.init(app) - } - fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { - self.canvas.should_render(app) - } - fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result { - self.canvas.render(app, tgt, buf, alpha) - } - fn frame_meta(&mut self) -> Option { - self.canvas.frame_meta() - } - fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> { - self.canvas.data_mut().modifiers = 0; - set_modifiers(app, 0); - self.canvas.pause(app) - } - fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> { - self.canvas.resume(app) - } -} diff --git a/src/overlays/mirror.rs b/src/overlays/mirror.rs deleted file mode 100644 index 734fe89..0000000 --- a/src/overlays/mirror.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::{ - sync::Arc, - task::{Context, Poll}, -}; - -use futures::{Future, FutureExt}; -use vulkano::image::view::ImageView; -use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult}; - -use crate::{ - backend::{ - common::OverlaySelector, - overlay::{ - ui_transform, FrameMeta, OverlayBackend, OverlayRenderer, OverlayState, ShouldRender, - SplitOverlayBackend, - }, - task::TaskType, - }, - graphics::CommandBuffers, - state::{AppSession, AppState}, -}; - -use super::screen::ScreenRenderer; -type PinnedSelectorFuture = core::pin::Pin< - Box>>, ->; - -pub struct MirrorRenderer { - name: Arc, - renderer: Option, - selector: Option, - last_extent: [u32; 3], -} -impl MirrorRenderer { - pub fn new(name: Arc) -> Self { - let selector = Box::pin(pipewire_select_screen(None, false, false, false, false)); - Self { - name, - renderer: None, - selector: Some(selector), - last_extent: [0; 3], - } - } -} - -impl OverlayRenderer for MirrorRenderer { - fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { - if let Some(mut selector) = self.selector.take() { - let maybe_pw_result = match selector - .poll_unpin(&mut Context::from_waker(futures::task::noop_waker_ref())) - { - Poll::Ready(result) => result, - Poll::Pending => { - self.selector = Some(selector); - return Ok(ShouldRender::Unable); - } - }; - - match maybe_pw_result { - Ok(pw_result) => { - let node_id = pw_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element - log::info!("{}: PipeWire node selected: {}", self.name.clone(), node_id); - let capture = PipewireCapture::new(self.name.clone(), node_id); - self.renderer = Some(ScreenRenderer::new_raw( - self.name.clone(), - Box::new(capture), - )); - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(self.name.clone()), - Box::new(|app, o| { - o.grabbable = true; - o.interactable = true; - o.reset(app, false); - }), - )); - } - Err(e) => { - log::warn!("Failed to create mirror due to PipeWire error: {e:?}"); - self.renderer = None; - // drop self - app.tasks - .enqueue(TaskType::DropOverlay(OverlaySelector::Name( - self.name.clone(), - ))); - } - } - } - self.renderer - .as_mut() - .map_or(Ok(ShouldRender::Unable), |r| r.should_render(app)) - } - fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result { - let mut result = false; - if let Some(renderer) = self.renderer.as_mut() { - result = renderer.render(app, tgt, buf, alpha)?; - if let Some(meta) = renderer.frame_meta() { - let extent = meta.extent; - if self.last_extent != extent { - self.last_extent = extent; - // resized - app.tasks.enqueue(TaskType::Overlay( - OverlaySelector::Name(self.name.clone()), - Box::new(move |_app, o| { - o.interaction_transform = ui_transform([extent[0], extent[1]]); - }), - )); - } - } - } - - Ok(result) - } - fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> { - if let Some(renderer) = self.renderer.as_mut() { - renderer.pause(app)?; - } - Ok(()) - } - fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> { - if let Some(renderer) = self.renderer.as_mut() { - renderer.resume(app)?; - } - Ok(()) - } - - fn frame_meta(&mut self) -> Option { - self.renderer.as_mut().and_then(ScreenRenderer::frame_meta) - } -} - -pub fn new_mirror( - name: Arc, - show_hide: bool, - session: &AppSession, -) -> (OverlayState, Box) { - let state = OverlayState { - name: name.clone(), - show_hide, - want_visible: true, - spawn_scale: 0.5 * session.config.desktop_view_scale, - ..Default::default() - }; - let backend = Box::new(SplitOverlayBackend { - renderer: Box::new(MirrorRenderer::new(name)), - ..Default::default() - }); - - (state, backend) -} diff --git a/src/overlays/screen.rs b/src/overlays/screen.rs deleted file mode 100644 index f576f63..0000000 --- a/src/overlays/screen.rs +++ /dev/null @@ -1,1216 +0,0 @@ -use core::slice; -use serde::{Deserialize, Serialize}; -use std::{ - f32::consts::PI, - ptr, - sync::{atomic::AtomicU64, Arc, LazyLock}, - time::Instant, -}; -use vulkano::{ - command_buffer::CommandBufferUsage, - device::Queue, - format::Format, - image::{sampler::Filter, view::ImageView, Image}, - pipeline::graphics::color_blend::AttachmentBlend, -}; -use wlx_capture::frame as wlx_frame; - -use wlx_capture::{ - frame::{FrameFormat, MouseMeta, WlxFrame}, - WlxCapture, -}; - -#[cfg(feature = "pipewire")] -use { - crate::config_io, - std::error::Error, - std::{path::PathBuf, task}, - wlx_capture::pipewire::PipewireCapture, - wlx_capture::pipewire::PipewireSelectScreenResult, -}; - -#[cfg(all(feature = "x11", feature = "pipewire"))] -use wlx_capture::pipewire::PipewireStream; - -#[cfg(feature = "wayland")] -use { - crate::config::AStrMapExt, - wlx_capture::{ - wayland::{wayland_client::protocol::wl_output, WlxClient, WlxOutput}, - wlr_dmabuf::WlrDmabufCapture, - wlr_screencopy::WlrScreencopyCapture, - }, -}; - -#[cfg(feature = "x11")] -use wlx_capture::xshm::{XshmCapture, XshmScreen}; - -use glam::{vec2, vec3a, Affine2, Affine3A, Quat, Vec2, Vec3}; - -use crate::{ - backend::{ - input::{Haptics, InteractionHandler, PointerHit, PointerMode}, - overlay::{ - FrameMeta, OverlayRenderer, OverlayState, Positioning, ShouldRender, - SplitOverlayBackend, - }, - }, - config::{def_pw_tokens, GeneralConfig, PwTokenMap}, - graphics::{fourcc_to_vk, CommandBuffers, WlxGraphics, WlxPipeline, WlxUploadsBuffer}, - hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT}, - state::{AppSession, AppState, KeyboardFocus, ScreenMeta}, -}; - -#[cfg(feature = "wayland")] -pub type WlxClientAlias = wlx_capture::wayland::WlxClient; - -#[cfg(not(feature = "wayland"))] -pub(crate) type WlxClientAlias = (); - -const CURSOR_SIZE: f32 = 16. / 1440.; - -static START: LazyLock = LazyLock::new(Instant::now); -static NEXT_MOVE: AtomicU64 = AtomicU64::new(0); - -fn can_move() -> bool { - START.elapsed().as_millis() as u64 > NEXT_MOVE.load(std::sync::atomic::Ordering::Relaxed) -} - -fn set_next_move(millis_from_now: u64) { - NEXT_MOVE.store( - START.elapsed().as_millis() as u64 + millis_from_now, - std::sync::atomic::Ordering::Relaxed, - ); -} - -pub struct ScreenInteractionHandler { - mouse_transform: Affine2, -} -impl ScreenInteractionHandler { - fn new(pos: Vec2, size: Vec2, transform: Transform) -> Self { - let transform = match transform { - Transform::_90 | Transform::Flipped90 => Affine2::from_cols( - vec2(0., size.y), - vec2(-size.x, 0.), - vec2(pos.x + size.x, pos.y), - ), - Transform::_180 | Transform::Flipped180 => Affine2::from_cols( - vec2(-size.x, 0.), - vec2(0., -size.y), - vec2(pos.x + size.x, pos.y + size.y), - ), - Transform::_270 | Transform::Flipped270 => Affine2::from_cols( - vec2(0., -size.y), - vec2(size.x, 0.), - vec2(pos.x, pos.y + size.y), - ), - _ => Affine2::from_cols(vec2(size.x, 0.), vec2(0., size.y), pos), - }; - - Self { - mouse_transform: transform, - } - } -} - -impl InteractionHandler for ScreenInteractionHandler { - fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option { - #[cfg(debug_assertions)] - log::trace!("Hover: {:?}", hit.uv); - if can_move() - && (!app.session.config.focus_follows_mouse_mode - || app.input_state.pointers[hit.pointer].now.move_mouse) - { - let pos = self.mouse_transform.transform_point2(hit.uv); - app.hid_provider.mouse_move(pos); - set_next_move(u64::from(app.session.config.mouse_move_interval_ms)); - } - None - } - fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { - let btn = match hit.mode { - PointerMode::Right => MOUSE_RIGHT, - PointerMode::Middle => MOUSE_MIDDLE, - _ => MOUSE_LEFT, - }; - - // Swap left and right buttons if left-handed mode is enabled - let btn = if app.session.config.left_handed_mouse { - match btn { - MOUSE_LEFT => MOUSE_RIGHT, - MOUSE_RIGHT => MOUSE_LEFT, - other => other, - } - } else { - btn - }; - - if pressed { - set_next_move(u64::from(app.session.config.click_freeze_time_ms)); - } - - app.hid_provider.send_button(btn, pressed); - - if !pressed { - return; - } - let pos = self.mouse_transform.transform_point2(hit.uv); - app.hid_provider.mouse_move(pos); - } - fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta_y: f32, delta_x: f32) { - app.hid_provider - .wheel((delta_y * 64.) as i32, (delta_x * 64.) as i32); - } - fn on_left(&mut self, _app: &mut AppState, _hand: usize) {} -} - -#[derive(Clone)] -struct ScreenPipeline { - mouse: Option>, - pipeline: Arc, - extentf: [f32; 2], -} - -impl ScreenPipeline { - fn new(extent: &[u32; 3], app: &mut AppState) -> anyhow::Result { - let Ok(shaders) = app.graphics.shared_shaders.read() else { - return Err(anyhow::anyhow!("Could not lock shared shaders for reading")); - }; - - let pipeline = app.graphics.create_pipeline( - shaders.get("vert_common").unwrap().clone(), // want panic - shaders.get("frag_screen").unwrap().clone(), // want panic - app.graphics.native_format, - Some(AttachmentBlend::default()), - )?; - - let extentf = [extent[0] as f32, extent[1] as f32]; - - Ok(Self { - mouse: None, - pipeline, - extentf, - }) - } - - fn ensure_mouse_initialized(&mut self, uploads: &mut WlxUploadsBuffer) -> anyhow::Result<()> { - if self.mouse.is_some() { - return Ok(()); - } - - #[rustfmt::skip] - let mouse_bytes = [ - 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, - ]; - - let mouse_tex = - uploads.texture2d_raw(4, 4, vulkano::format::Format::R8G8B8A8_UNORM, &mouse_bytes)?; - self.mouse = Some(ImageView::new_default(mouse_tex)?); - Ok(()) - } - - fn render( - &mut self, - image: Arc, - mouse: Option, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result<()> { - let view = ImageView::new_default(image)?; - let set0 = self - .pipeline - .uniform_sampler(0, view, app.graphics.texture_filtering)?; - let set1 = self.pipeline.uniform_buffer(1, vec![alpha])?; - let pass = self - .pipeline - .create_pass_for_target(tgt.clone(), vec![set0, set1])?; - - let mut cmd = app - .graphics - .create_command_buffer(CommandBufferUsage::OneTimeSubmit)?; - cmd.begin_rendering(tgt)?; - cmd.run_ref(&pass)?; - - if let (Some(mouse), Some(mouse_view)) = (mouse, self.mouse.clone()) { - let size = CURSOR_SIZE * self.extentf[1]; - let half_size = size * 0.5; - - let vertex_buffer = app.graphics.upload_verts( - self.extentf[0], - self.extentf[1], - mouse.x.mul_add(self.extentf[0], -half_size), - mouse.y.mul_add(self.extentf[1], -half_size), - size, - size, - )?; - - let set0 = self - .pipeline - .uniform_sampler(0, mouse_view, Filter::Nearest)?; - - let set1 = self.pipeline.uniform_buffer(1, vec![alpha])?; - - let pass = self.pipeline.create_pass( - self.extentf, - vertex_buffer, - app.graphics.quad_indices.clone(), - vec![set0, set1], - )?; - - cmd.run_ref(&pass)?; - } - - cmd.end_rendering()?; - buf.push(cmd.build()?); - Ok(()) - } -} - -macro_rules! new_wlx_capture { - ($capture_queue:expr, $capture:expr) => { - if $capture_queue.is_none() { - Box::new(MainThreadWlxCapture::new($capture)) as Box> - } else { - Box::new($capture) as Box> - } - }; -} - -pub struct ScreenRenderer { - name: Arc, - capture: Box>, - pipeline: Option, - cur_frame: Option, - meta: Option, -} - -impl ScreenRenderer { - pub fn new_raw( - name: Arc, - capture: Box>, - ) -> Self { - Self { - name, - capture, - pipeline: None, - cur_frame: None, - meta: None, - } - } - - #[cfg(feature = "wayland")] - pub fn new_wlr_dmabuf(output: &WlxOutput, app: &AppState) -> Option { - let client = WlxClient::new()?; - let capture = new_wlx_capture!( - app.graphics.capture_queue, - WlrDmabufCapture::new(client, output.id) - ); - Some(Self::new_raw(output.name.clone(), capture)) - } - - #[cfg(feature = "wayland")] - pub fn new_wlr_screencopy(output: &WlxOutput, app: &AppState) -> Option { - let client = WlxClient::new()?; - let capture = new_wlx_capture!( - app.graphics.capture_queue, - WlrScreencopyCapture::new(client, output.id) - ); - Some(Self::new_raw(output.name.clone(), capture)) - } - - #[cfg(feature = "wayland")] - pub fn new_pw( - output: &WlxOutput, - token: Option<&str>, - app: &AppState, - ) -> anyhow::Result<(Self, Option /* pipewire restore token */)> { - let name = output.name.clone(); - let embed_mouse = !app.session.config.double_cursor_fix; - - let select_screen_result = select_pw_screen( - &format!( - "Now select: {} {} {} @ {},{}", - &output.name, - &output.make, - &output.model, - &output.logical_pos.0, - &output.logical_pos.1 - ), - token, - embed_mouse, - true, - true, - false, - )?; - - let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element - - let capture = new_wlx_capture!( - app.graphics.capture_queue, - PipewireCapture::new(name, node_id) - ); - Ok(( - Self::new_raw(output.name.clone(), capture), - select_screen_result.restore_token, - )) - } - - #[cfg(feature = "x11")] - pub fn new_xshm(screen: Arc, app: &AppState) -> Self { - let capture = - new_wlx_capture!(app.graphics.capture_queue, XshmCapture::new(screen.clone())); - Self::new_raw(screen.name.clone(), capture) - } -} - -#[derive(Clone)] -pub struct WlxCaptureIn { - name: Arc, - graphics: Arc, - queue: Arc, -} - -#[derive(Clone)] -pub struct WlxCaptureOut { - image: Arc, - format: FrameFormat, - mouse: Option, -} - -fn upload_image( - me: &WlxCaptureIn, - width: u32, - height: u32, - format: Format, - data: &[u8], -) -> Option> { - let mut upload = match me - .graphics - .create_uploads_command_buffer(me.queue.clone(), CommandBufferUsage::OneTimeSubmit) - { - Ok(x) => x, - Err(e) => { - log::error!("{}: Could not create vkCommandBuffer: {:?}", me.name, e); - return None; - } - }; - let image = match upload.texture2d_raw(width, height, format, data) { - Ok(x) => x, - Err(e) => { - log::error!("{}: Could not create vkImage: {:?}", me.name, e); - return None; - } - }; - - if let Err(e) = upload.build_and_execute_now() { - log::error!("{}: Could not execute upload: {:?}", me.name, e); - return None; - } - - Some(image) -} - -fn receive_callback(me: &WlxCaptureIn, frame: wlx_frame::WlxFrame) -> Option { - match frame { - WlxFrame::Dmabuf(frame) => { - if !frame.is_valid() { - log::error!("{}: Invalid frame", me.name); - return None; - } - log::trace!("{}: New DMA-buf frame", me.name); - let format = frame.format; - match me.graphics.dmabuf_texture(frame) { - Ok(image) => Some(WlxCaptureOut { - image, - format, - mouse: None, - }), - Err(e) => { - log::error!("{}: Failed to create DMA-buf vkImage: {}", me.name, e); - None - } - } - } - WlxFrame::MemFd(frame) => { - let Some(fd) = frame.plane.fd else { - log::error!("{}: No fd in MemFd frame", me.name); - return None; - }; - - let format = match fourcc_to_vk(frame.format.fourcc) { - Ok(x) => x, - Err(e) => { - log::error!("{}: {}", me.name, e); - return None; - } - }; - - let len = frame.plane.stride as usize * frame.format.height as usize; - let offset = i64::from(frame.plane.offset); - - let map = unsafe { - libc::mmap( - ptr::null_mut(), - len, - libc::PROT_READ, - libc::MAP_SHARED, - fd, - offset, - ) - } as *const u8; - - let data = unsafe { slice::from_raw_parts(map, len) }; - - let image = { - let maybe_image = - upload_image(me, frame.format.width, frame.format.height, format, data); - - unsafe { libc::munmap(map as *mut _, len) }; - maybe_image - }?; - - Some(WlxCaptureOut { - image, - format: frame.format, - mouse: None, - }) - } - WlxFrame::MemPtr(frame) => { - log::trace!("{}: New MemPtr frame", me.name); - - let format = match fourcc_to_vk(frame.format.fourcc) { - Ok(x) => x, - Err(e) => { - log::error!("{}: {}", me.name, e); - return None; - } - }; - - let data = unsafe { slice::from_raw_parts(frame.ptr as *const u8, frame.size) }; - let image = upload_image(me, frame.format.width, frame.format.height, format, data)?; - - Some(WlxCaptureOut { - image, - format: frame.format, - mouse: frame.mouse, - }) - } - } -} - -impl OverlayRenderer for ScreenRenderer { - fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - Ok(()) - } - fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { - if !self.capture.is_ready() { - let supports_dmabuf = app - .graphics - .device - .enabled_extensions() - .ext_external_memory_dma_buf - && self.capture.supports_dmbuf(); - - let allow_dmabuf = &*app.session.config.capture_method != "pw_fallback" - && &*app.session.config.capture_method != "screencopy"; - - let capture_method = app.session.config.capture_method.clone(); - - let dmabuf_formats = if !supports_dmabuf { - log::info!("Capture method does not support DMA-buf"); - if app.graphics.capture_queue.is_none() { - log::warn!("Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."); - } - &Vec::new() - } else if !allow_dmabuf { - log::info!("Not using DMA-buf capture due to {capture_method}"); - if app.graphics.capture_queue.is_none() { - log::warn!("Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."); - } - &Vec::new() - } else { - log::warn!( - "Using DMA-buf capture. If screens are blank for you, switch to SHM using:" - ); - log::warn!("echo 'capture_method: pw_fallback' > ~/.config/wlxoverlay/conf.d/pw_fallback.yaml"); - - &app.graphics.drm_formats - }; - - let user_data = WlxCaptureIn { - name: self.name.clone(), - graphics: app.graphics.clone(), - queue: app - .graphics - .capture_queue - .as_ref() - .unwrap_or_else(|| &app.graphics.transfer_queue) - .clone(), - }; - - self.capture - .init(dmabuf_formats, user_data, receive_callback); - self.capture.request_new_frame(); - return Ok(ShouldRender::Unable); - } - - if let Some(frame) = self.capture.receive() { - self.meta = Some(FrameMeta { - extent: extent_from_format(frame.format, &app.session.config), - transform: affine_from_format(&frame.format), - format: frame.image.format(), - }); - self.cur_frame = Some(frame); - } - - if let (Some(capture), None) = (self.cur_frame.as_ref(), self.pipeline.as_ref()) { - self.pipeline = Some({ - let mut pipeline = ScreenPipeline::new(&capture.image.extent(), app)?; - let mut upload = app.graphics.create_uploads_command_buffer( - app.graphics.transfer_queue.clone(), - CommandBufferUsage::OneTimeSubmit, - )?; - pipeline.ensure_mouse_initialized(&mut upload)?; - upload.build_and_execute_now()?; - pipeline - }); - } - - if self.cur_frame.is_some() { - Ok(ShouldRender::Should) - } else { - Ok(ShouldRender::Unable) - } - } - fn render( - &mut self, - app: &mut AppState, - tgt: Arc, - buf: &mut CommandBuffers, - alpha: f32, - ) -> anyhow::Result { - let Some(capture) = self.cur_frame.take() else { - return Ok(false); - }; - - // want panic; must be Some if cur_frame is also Some - self.pipeline.as_mut().unwrap().render( - capture.image, - capture.mouse, - app, - tgt, - buf, - alpha, - )?; - - self.capture.request_new_frame(); - Ok(true) - } - fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - self.capture.pause(); - Ok(()) - } - fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> { - self.capture.resume(); - Ok(()) - } - fn frame_meta(&mut self) -> Option { - self.meta - } -} - -#[cfg(feature = "wayland")] -#[allow(clippy::useless_let_if_seq)] -pub fn create_screen_renderer_wl( - output: &WlxOutput, - has_wlr_dmabuf: bool, - has_wlr_screencopy: bool, - pw_token_store: &mut PwTokenMap, - app: &AppState, -) -> Option { - let mut capture: Option = None; - if (&*app.session.config.capture_method == "wlr-dmabuf") && has_wlr_dmabuf { - log::info!("{}: Using Wlr DMA-Buf", &output.name); - capture = ScreenRenderer::new_wlr_dmabuf(output, app); - } - - if &*app.session.config.capture_method == "screencopy" && has_wlr_screencopy { - log::info!("{}: Using Wlr Screencopy Wl-SHM", &output.name); - capture = ScreenRenderer::new_wlr_screencopy(output, app); - } - - if capture.is_none() { - log::info!("{}: Using Pipewire capture", &output.name); - - let display_name = &*output.name; - - // Find existing token by display - let token = pw_token_store - .arc_get(display_name) - .map(std::string::String::as_str); - - if let Some(t) = token { - log::info!("Found existing Pipewire token for display {display_name}: {t}"); - } - - match ScreenRenderer::new_pw(output, token, app) { - Ok((renderer, restore_token)) => { - capture = Some(renderer); - - if let Some(token) = restore_token { - if pw_token_store.arc_set(display_name.into(), token.clone()) { - log::info!("Adding Pipewire token {token}"); - } - } - } - Err(e) => { - log::warn!( - "{}: Failed to create Pipewire capture: {:?}", - &output.name, - e - ); - } - } - } - capture -} - -pub fn create_screen_interaction( - logical_pos: Vec2, - logical_size: Vec2, - transform: Transform, -) -> ScreenInteractionHandler { - ScreenInteractionHandler::new(logical_pos, logical_size, transform) -} - -fn create_screen_state( - name: Arc, - res: (i32, i32), - transform: Transform, - session: &AppSession, -) -> OverlayState { - let angle = if session.config.upright_screen_fix { - match transform { - Transform::_90 | Transform::Flipped90 => PI / 2., - Transform::_180 | Transform::Flipped180 => PI, - Transform::_270 | Transform::Flipped270 => -PI / 2., - _ => 0., - } - } else { - 0. - }; - - let center = Vec2 { x: 0.5, y: 0.5 }; - let interaction_transform = match transform { - Transform::_90 | Transform::Flipped90 => Affine2::from_cols( - Vec2::NEG_Y * (res.0 as f32 / res.1 as f32), - Vec2::NEG_X, - center, - ), - Transform::_180 | Transform::Flipped180 => Affine2::from_cols( - Vec2::NEG_X, - Vec2::NEG_Y * (-res.0 as f32 / res.1 as f32), - center, - ), - Transform::_270 | Transform::Flipped270 => { - Affine2::from_cols(Vec2::Y * (res.0 as f32 / res.1 as f32), Vec2::X, center) - } - _ if res.1 > res.0 => { - // Xorg upright screens - Affine2::from_cols(Vec2::X * (res.1 as f32 / res.0 as f32), Vec2::NEG_Y, center) - } - _ => Affine2::from_cols(Vec2::X, Vec2::NEG_Y * (res.0 as f32 / res.1 as f32), center), - }; - - OverlayState { - name, - keyboard_focus: Some(KeyboardFocus::PhysicalScreen), - grabbable: true, - recenter: true, - positioning: Positioning::Anchored, - interactable: true, - spawn_scale: 1.5 * session.config.desktop_view_scale, - spawn_point: vec3a(0., 0.5, 0.), - spawn_rotation: Quat::from_axis_angle(Vec3::Z, angle), - interaction_transform, - ..Default::default() - } -} - -#[derive(Deserialize, Serialize, Default)] -pub struct TokenConf { - #[serde(default = "def_pw_tokens")] - pub pw_tokens: PwTokenMap, -} - -#[cfg(feature = "pipewire")] -fn get_pw_token_path() -> PathBuf { - let mut path = config_io::ConfigRoot::Generic.get_conf_d_path(); - path.push("pw_tokens.yaml"); - path -} - -#[cfg(feature = "pipewire")] -pub fn save_pw_token_config(tokens: PwTokenMap) -> Result<(), Box> { - let conf = TokenConf { pw_tokens: tokens }; - let yaml = serde_yaml::to_string(&conf)?; - std::fs::write(get_pw_token_path(), yaml)?; - - Ok(()) -} - -#[cfg(feature = "pipewire")] -pub fn load_pw_token_config() -> Result> { - let yaml = std::fs::read_to_string(get_pw_token_path())?; - let conf: TokenConf = serde_yaml::from_str(yaml.as_str())?; - Ok(conf.pw_tokens) -} - -pub struct ScreenCreateData { - pub screens: Vec<(ScreenMeta, OverlayState, Box)>, -} - -#[cfg(not(feature = "wayland"))] -pub fn create_screens_wayland(_wl: &mut WlxClientAlias, _app: &AppState) -> ScreenCreateData { - ScreenCreateData { - screens: Vec::default(), - } -} - -#[cfg(feature = "wayland")] -pub fn create_screens_wayland(wl: &mut WlxClientAlias, app: &mut AppState) -> ScreenCreateData { - let mut screens = vec![]; - - // Load existing Pipewire tokens from file - let mut pw_tokens: PwTokenMap = load_pw_token_config().unwrap_or_default(); - - let pw_tokens_copy = pw_tokens.clone(); - let has_wlr_dmabuf = wl.maybe_wlr_dmabuf_mgr.is_some(); - let has_wlr_screencopy = wl.maybe_wlr_screencopy_mgr.is_some(); - - for (id, output) in &wl.outputs { - if app.screens.iter().any(|s| s.name == output.name) { - continue; - } - - log::info!( - "{}: Init screen of res {:?}, logical {:?} at {:?}", - output.name, - output.size, - output.logical_size, - output.logical_pos, - ); - - if let Some(renderer) = create_screen_renderer_wl( - output, - has_wlr_dmabuf, - has_wlr_screencopy, - &mut pw_tokens, - app, - ) { - let logical_pos = vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32); - let logical_size = vec2(output.logical_size.0 as f32, output.logical_size.1 as f32); - let transform = output.transform.into(); - let interaction = create_screen_interaction(logical_pos, logical_size, transform); - - let state = - create_screen_state(output.name.clone(), output.size, transform, &app.session); - - let meta = ScreenMeta { - name: wl.outputs[id].name.clone(), - id: state.id, - native_handle: *id, - }; - - let backend = Box::new(SplitOverlayBackend { - renderer: Box::new(renderer), - interaction: Box::new(interaction), - }); - screens.push((meta, state, backend)); - } - } - - if pw_tokens_copy != pw_tokens { - // Token list changed, re-create token config file - if let Err(err) = save_pw_token_config(pw_tokens) { - log::error!("Failed to save Pipewire token config: {err}"); - } - } - - let extent = wl.get_desktop_extent(); - let origin = wl.get_desktop_origin(); - - app.hid_provider - .set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32)); - app.hid_provider - .set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32)); - - ScreenCreateData { screens } -} - -#[cfg(not(feature = "x11"))] -pub fn create_screens_xshm(_app: &mut AppState) -> anyhow::Result { - anyhow::bail!("X11 support not enabled") -} - -#[cfg(not(all(feature = "x11", feature = "pipewire")))] -pub fn create_screens_x11pw(_app: &mut AppState) -> anyhow::Result { - anyhow::bail!("Pipewire support not enabled") -} - -#[cfg(all(feature = "x11", feature = "pipewire"))] -pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result { - use wlx_capture::xshm::xshm_get_monitors; - - // Load existing Pipewire tokens from file - let mut pw_tokens: PwTokenMap = load_pw_token_config().unwrap_or_default(); - let pw_tokens_copy = pw_tokens.clone(); - let token = pw_tokens.arc_get("x11").map(std::string::String::as_str); - let embed_mouse = !app.session.config.double_cursor_fix; - - let select_screen_result = select_pw_screen( - "Select ALL screens on the screencast pop-up!", - token, - embed_mouse, - true, - true, - true, - )?; - - if let Some(restore_token) = select_screen_result.restore_token { - if pw_tokens.arc_set("x11".into(), restore_token.clone()) { - log::info!("Adding Pipewire token {restore_token}"); - } - } - if pw_tokens_copy != pw_tokens { - // Token list changed, re-create token config file - if let Err(err) = save_pw_token_config(pw_tokens) { - log::error!("Failed to save Pipewire token config: {err}"); - } - } - - let monitors = match xshm_get_monitors() { - Ok(m) => m, - Err(e) => { - anyhow::bail!(e.to_string()); - } - }; - log::info!("Got {} monitors", monitors.len()); - log::info!("Got {} streams", select_screen_result.streams.len()); - - let mut extent = vec2(0., 0.); - let screens = select_screen_result - .streams - .into_iter() - .enumerate() - .map(|(i, s)| { - let m = best_match(&s, monitors.iter().map(AsRef::as_ref)).unwrap(); - log::info!("Stream {i} is {}", m.name); - extent.x = extent.x.max((m.monitor.x() + m.monitor.width()) as f32); - extent.y = extent.y.max((m.monitor.y() + m.monitor.height()) as f32); - - let size = (m.monitor.width(), m.monitor.height()); - let interaction = create_screen_interaction( - vec2(m.monitor.x() as f32, m.monitor.y() as f32), - vec2(m.monitor.width() as f32, m.monitor.height() as f32), - Transform::Normal, - ); - - let state = create_screen_state(m.name.clone(), size, Transform::Normal, &app.session); - - let meta = ScreenMeta { - name: m.name.clone(), - id: state.id, - native_handle: 0, - }; - - let renderer = ScreenRenderer::new_raw( - m.name.clone(), - new_wlx_capture!( - app.graphics.capture_queue, - PipewireCapture::new(m.name.clone(), s.node_id) - ), - ); - - let backend = Box::new(SplitOverlayBackend { - renderer: Box::new(renderer), - interaction: Box::new(interaction), - }); - (meta, state, backend) - }) - .collect(); - - app.hid_provider.set_desktop_extent(extent); - app.hid_provider.set_desktop_origin(vec2(0.0, 0.0)); - - Ok(ScreenCreateData { screens }) -} - -#[cfg(feature = "x11")] -pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result { - use wlx_capture::xshm::xshm_get_monitors; - - let mut extent = vec2(0., 0.); - - let monitors = match xshm_get_monitors() { - Ok(m) => m, - Err(e) => { - anyhow::bail!(e.to_string()); - } - }; - - let screens = monitors - .into_iter() - .map(|s| { - extent.x = extent.x.max((s.monitor.x() + s.monitor.width()) as f32); - extent.y = extent.y.max((s.monitor.y() + s.monitor.height()) as f32); - - let size = (s.monitor.width(), s.monitor.height()); - let pos = (s.monitor.x(), s.monitor.y()); - let renderer = ScreenRenderer::new_xshm(s.clone(), app); - - log::info!( - "{}: Init X11 screen of res {:?} at {:?}", - s.name.clone(), - size, - pos, - ); - - let interaction = create_screen_interaction( - vec2(s.monitor.x() as f32, s.monitor.y() as f32), - vec2(size.0 as f32, size.1 as f32), - Transform::Normal, - ); - - let state = create_screen_state(s.name.clone(), size, Transform::Normal, &app.session); - - let meta = ScreenMeta { - name: s.name.clone(), - id: state.id, - native_handle: 0, - }; - - let backend = Box::new(SplitOverlayBackend { - renderer: Box::new(renderer), - interaction: Box::new(interaction), - }); - (meta, state, backend) - }) - .collect(); - - app.hid_provider.set_desktop_extent(extent); - app.hid_provider.set_desktop_origin(vec2(0.0, 0.0)); - - Ok(ScreenCreateData { screens }) -} - -#[allow(unused)] -#[derive(Clone, Copy)] -pub enum Transform { - Normal, - _90, - _180, - _270, - Flipped, - Flipped90, - Flipped180, - Flipped270, -} - -#[cfg(feature = "wayland")] -impl From for Transform { - fn from(t: wl_output::Transform) -> Self { - match t { - wl_output::Transform::_90 => Self::_90, - wl_output::Transform::_180 => Self::_180, - wl_output::Transform::_270 => Self::_270, - wl_output::Transform::Flipped => Self::Flipped, - wl_output::Transform::Flipped90 => Self::Flipped90, - wl_output::Transform::Flipped180 => Self::Flipped180, - wl_output::Transform::Flipped270 => Self::Flipped270, - _ => Self::Normal, - } - } -} - -fn extent_from_format(fmt: FrameFormat, config: &GeneralConfig) -> [u32; 3] { - // screens above a certain resolution will have severe aliasing - let height_limit = if config.screen_render_down { - u32::from(config.screen_max_height.min(2560)) - } else { - 2560 - }; - - let h = fmt.height.min(height_limit); - let w = (fmt.width as f32 / fmt.height as f32 * h as f32) as u32; - [w, h, 1] -} - -fn affine_from_format(format: &FrameFormat) -> Affine3A { - const FLIP_X: Vec3 = Vec3 { - x: -1.0, - y: 1.0, - z: 1.0, - }; - - match format.transform { - wlx_frame::Transform::Rotated90 => Affine3A::from_rotation_z(-PI / 2.0), - wlx_frame::Transform::Rotated180 => Affine3A::from_rotation_z(PI), - wlx_frame::Transform::Rotated270 => Affine3A::from_rotation_z(PI / 2.0), - wlx_frame::Transform::Flipped => Affine3A::from_scale(FLIP_X), - wlx_frame::Transform::Flipped90 => { - Affine3A::from_scale(FLIP_X) * Affine3A::from_rotation_z(-PI / 2.0) - } - wlx_frame::Transform::Flipped180 => { - Affine3A::from_scale(FLIP_X) * Affine3A::from_rotation_z(PI) - } - wlx_frame::Transform::Flipped270 => { - Affine3A::from_scale(FLIP_X) * Affine3A::from_rotation_z(PI / 2.0) - } - _ => Affine3A::IDENTITY, - } -} - -#[cfg(all(feature = "pipewire", feature = "x11"))] -fn best_match<'a>( - stream: &PipewireStream, - mut streams: impl Iterator, -) -> Option<&'a XshmScreen> { - let mut best = streams.next(); - log::debug!("stream: {:?}", stream.position); - log::debug!("first: {:?}", best.map(|b| &b.monitor)); - let Some(position) = stream.position else { - return best; - }; - - let mut best_dist = best.map_or(i32::MAX, |b| { - (b.monitor.x() - position.0).abs() + (b.monitor.y() - position.1).abs() - }); - for stream in streams { - log::debug!("checking: {:?}", stream.monitor); - let dist = - (stream.monitor.x() - position.0).abs() + (stream.monitor.y() - position.1).abs(); - if dist < best_dist { - best = Some(stream); - best_dist = dist; - } - } - log::debug!("best: {:?}", best.map(|b| &b.monitor)); - best -} - -#[cfg(feature = "pipewire")] -#[allow(clippy::fn_params_excessive_bools)] -fn select_pw_screen( - instructions: &str, - token: Option<&str>, - embed_mouse: bool, - screens_only: bool, - persist: bool, - multiple: bool, -) -> Result { - use crate::backend::notifications::DbusNotificationSender; - use std::time::Duration; - use wlx_capture::pipewire::pipewire_select_screen; - - let future = async move { - let print_at = Instant::now() + Duration::from_millis(250); - let mut notify = None; - - let f = pipewire_select_screen(token, embed_mouse, screens_only, persist, multiple); - futures::pin_mut!(f); - - loop { - match futures::poll!(&mut f) { - task::Poll::Ready(result) => return result, - task::Poll::Pending => { - if Instant::now() >= print_at { - log::info!("{instructions}"); - if let Ok(sender) = DbusNotificationSender::new() { - if let Ok(id) = sender.notify_send(instructions, "", 2, 0, 0, true) { - notify = Some((sender, id)); - } - } - break; - } - futures::future::lazy(|_| { - std::thread::sleep(Duration::from_millis(10)); - }) - .await; - } - } - } - - let result = f.await; - if let Some((sender, id)) = notify { - let _ = sender.notify_close(id); - } - result - }; - - futures::executor::block_on(future) -} - -// Used when a separate GPU queue is not available -// In this case, receive_callback needs to run on the main thread -struct MainThreadWlxCapture -where - T: WlxCapture<(), WlxFrame>, -{ - inner: T, - data: Option, -} - -impl MainThreadWlxCapture -where - T: WlxCapture<(), WlxFrame>, -{ - pub const fn new(inner: T) -> Self { - Self { inner, data: None } - } -} - -impl WlxCapture for MainThreadWlxCapture -where - T: WlxCapture<(), WlxFrame>, -{ - fn init( - &mut self, - dmabuf_formats: &[wlx_frame::DrmFormat], - user_data: WlxCaptureIn, - _: fn(&WlxCaptureIn, WlxFrame) -> Option, - ) { - self.data = Some(user_data); - self.inner.init(dmabuf_formats, (), receive_callback_dummy); - } - fn is_ready(&self) -> bool { - self.inner.is_ready() - } - fn request_new_frame(&mut self) { - self.inner.request_new_frame(); - } - fn pause(&mut self) { - self.inner.pause(); - } - fn resume(&mut self) { - self.inner.resume(); - } - fn receive(&mut self) -> Option { - self.inner - .receive() - .and_then(|frame| receive_callback(self.data.as_ref().unwrap(), frame)) - } - fn supports_dmbuf(&self) -> bool { - self.inner.supports_dmbuf() - } -} - -#[allow(clippy::trivially_copy_pass_by_ref, clippy::unnecessary_wraps)] -const fn receive_callback_dummy(_: &(), frame: wlx_frame::WlxFrame) -> Option { - Some(frame) -} diff --git a/src/overlays/toast.rs b/src/overlays/toast.rs deleted file mode 100644 index 7ed0893..0000000 --- a/src/overlays/toast.rs +++ /dev/null @@ -1,238 +0,0 @@ -use std::{ - f32::consts::PI, - ops::Add, - sync::{Arc, LazyLock}, - time::Instant, -}; - -use glam::{vec3a, Quat}; -use idmap_derive::IntegerId; -use serde::{Deserialize, Serialize}; - -use crate::{ - backend::{ - common::OverlaySelector, - overlay::{OverlayBackend, OverlayState, Positioning, Z_ORDER_TOAST}, - task::TaskType, - }, - gui::{canvas::builder::CanvasBuilder, color_parse}, - state::{AppState, LeftRight}, -}; - -const FONT_SIZE: isize = 16; -const PADDING: (f32, f32) = (25., 7.); -const PIXELS_TO_METERS: f32 = 1. / 2000.; -static TOAST_NAME: LazyLock> = LazyLock::new(|| "toast".into()); - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub enum DisplayMethod { - Hide, - Center, - Watch, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, IntegerId, Serialize, Deserialize)] -pub enum ToastTopic { - System, - DesktopNotification, - XSNotification, - IpdChange, -} - -pub struct Toast { - pub title: Arc, - pub body: Arc, - pub opacity: f32, - pub timeout: f32, - pub sound: bool, - pub topic: ToastTopic, -} - -#[allow(dead_code)] -impl Toast { - pub const fn new(topic: ToastTopic, title: Arc, body: Arc) -> Self { - Self { - title, - body, - opacity: 1.0, - timeout: 3.0, - sound: false, - topic, - } - } - pub const fn with_timeout(mut self, timeout: f32) -> Self { - self.timeout = timeout; - self - } - pub const fn with_opacity(mut self, opacity: f32) -> Self { - self.opacity = opacity; - self - } - pub const fn with_sound(mut self, sound: bool) -> Self { - self.sound = sound; - self - } - pub fn submit(self, app: &mut AppState) { - self.submit_at(app, Instant::now()); - } - pub fn submit_at(self, app: &mut AppState, instant: Instant) { - let selector = OverlaySelector::Name(TOAST_NAME.clone()); - - let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout)); - - let has_sound = self.sound && app.session.config.notifications_sound_enabled; - if has_sound { - app.audio.play(app.toast_sound); - } - - // drop any toast that was created before us. - // (DropOverlay only drops overlays that were - // created before current frame) - app.tasks - .enqueue_at(TaskType::DropOverlay(selector.clone()), instant); - - // CreateOverlay only creates the overlay if - // the selector doesn't exist yet, so in case - // multiple toasts are submitted for the same - // frame, only the first one gets created - app.tasks.enqueue_at( - TaskType::CreateOverlay( - selector, - Box::new(move |app| { - let mut maybe_toast = new_toast(self, app); - if let Some((state, _)) = maybe_toast.as_mut() { - state.auto_movement(app); - app.tasks.enqueue_at( - // at timeout, drop the overlay by ID instead - // in order to avoid dropping any newer toasts - TaskType::DropOverlay(OverlaySelector::Id(state.id)), - destroy_at, - ); - } - maybe_toast - }), - ), - instant, - ); - } -} - -fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box)> { - let current_method = app - .session - .toast_topics - .get(toast.topic) - .copied() - .unwrap_or(DisplayMethod::Hide); - - let (spawn_point, spawn_rotation, positioning) = match current_method { - DisplayMethod::Hide => return None, - DisplayMethod::Center => ( - vec3a(0., -0.2, -0.5), - Quat::IDENTITY, - Positioning::FollowHead { lerp: 0.1 }, - ), - DisplayMethod::Watch => { - let mut watch_pos = app.session.config.watch_pos + vec3a(-0.005, -0.05, 0.02); - let mut watch_rot = app.session.config.watch_rot; - let relative_to = match app.session.config.watch_hand { - LeftRight::Left => Positioning::FollowHand { hand: 0, lerp: 1.0 }, - LeftRight::Right => { - watch_pos.x = -watch_pos.x; - watch_rot = watch_rot * Quat::from_rotation_x(PI) * Quat::from_rotation_z(PI); - Positioning::FollowHand { hand: 1, lerp: 1.0 } - } - }; - (watch_pos, watch_rot, relative_to) - } - }; - - let title = if toast.title.is_empty() { - "Notification".into() - } else { - toast.title - }; - - let mut size = if toast.body.is_empty() { - let (w, h) = app - .fc - .get_text_size(&title, FONT_SIZE, app.graphics.clone()) - .ok()?; - (w, h + 20.) - } else { - let (w0, _) = app - .fc - .get_text_size(&title, FONT_SIZE, app.graphics.clone()) - .ok()?; - let (w1, h1) = app - .fc - .get_text_size(&toast.body, FONT_SIZE, app.graphics.clone()) - .ok()?; - (w0.max(w1), h1 + 50.) - }; - - let og_width = size.0; - size.0 += PADDING.0 * 2.; - - let mut canvas = CanvasBuilder::<(), ()>::new( - size.0 as _, - size.1 as _, - app.graphics.clone(), - app.graphics.native_format, - (), - ) - .ok()?; - - canvas.font_size = FONT_SIZE; - canvas.fg_color = color_parse("#cad3f5").unwrap(); // want panic - canvas.bg_color = color_parse("#1e2030").unwrap(); // want panic - canvas.panel(0., 0., size.0, size.1, 16.); - - if toast.body.is_empty() { - canvas.label_centered(PADDING.0, 0., og_width, size.1, 16., title); - } else { - canvas.label(PADDING.0, 54., og_width, size.1 - 54., 3., toast.body); - - canvas.fg_color = color_parse("#b8c0e0").unwrap(); // want panic - canvas.bg_color = color_parse("#24273a").unwrap(); // want panic - canvas.panel(0., 0., size.0, 30., 16.); - canvas.label_centered(PADDING.0, 16., og_width, FONT_SIZE as f32 + 2., 16., title); - } - - let state = OverlayState { - name: TOAST_NAME.clone(), - want_visible: true, - spawn_scale: size.0 * PIXELS_TO_METERS, - spawn_rotation, - spawn_point, - z_order: Z_ORDER_TOAST, - positioning, - ..Default::default() - }; - let backend = Box::new(canvas.build()); - - Some((state, backend)) -} - -fn msg_err(app: &mut AppState, message: &str) { - Toast::new(ToastTopic::System, "Error".into(), message.into()) - .with_timeout(3.) - .submit(app); -} - -// Display the same error in the terminal and as a toast in VR. -// Formatted as "Failed to XYZ: Object is not defined" -pub fn error_toast(app: &mut AppState, title: &str, err: ErrorType) -where - ErrorType: std::fmt::Display + std::fmt::Debug, -{ - log::error!("{title}: {err:?}"); // More detailed version (use Debug) - - // Brief version (use Display) - msg_err(app, &format!("{title}: {err}")); -} - -pub fn error_toast_str(app: &mut AppState, message: &str) { - log::error!("{message}"); - msg_err(app, message); -} diff --git a/src/overlays/watch.rs b/src/overlays/watch.rs deleted file mode 100644 index cf29fb8..0000000 --- a/src/overlays/watch.rs +++ /dev/null @@ -1,80 +0,0 @@ -use glam::Vec3A; - -use crate::{ - backend::overlay::{ui_transform, OverlayData, OverlayState, Positioning, Z_ORDER_WATCH}, - config::{load_known_yaml, ConfigType}, - gui::{ - canvas::Canvas, - modular::{modular_canvas, ModularData, ModularUiConfig}, - }, - state::AppState, -}; - -pub const WATCH_NAME: &str = "watch"; - -pub fn create_watch(state: &mut AppState) -> anyhow::Result> -where - O: Default, -{ - let config = load_known_yaml::(ConfigType::Watch); - - let positioning = Positioning::FollowHand { - hand: state.session.config.watch_hand as _, - lerp: 1.0, - }; - - Ok(OverlayData { - state: OverlayState { - name: WATCH_NAME.into(), - want_visible: true, - interactable: true, - z_order: Z_ORDER_WATCH, - spawn_scale: config.width, - spawn_point: state.session.config.watch_pos, - spawn_rotation: state.session.config.watch_rot, - interaction_transform: ui_transform(config.size), - positioning, - ..Default::default() - }, - backend: Box::new(create_watch_canvas(Some(config), state)?), - ..Default::default() - }) -} - -pub fn create_watch_canvas( - config: Option, - state: &mut AppState, -) -> anyhow::Result> { - let config = config.unwrap_or_else(|| load_known_yaml::(ConfigType::Watch)); - - modular_canvas(config.size, &config.elements, state) -} - -pub fn watch_fade(app: &mut AppState, watch: &mut OverlayData) -where - D: Default, -{ - if watch.state.saved_transform.is_some() { - watch.state.want_visible = false; - return; - } - - let to_hmd = (watch.state.transform.translation - app.input_state.hmd.translation).normalize(); - let watch_normal = watch - .state - .transform - .transform_vector3a(Vec3A::NEG_Z) - .normalize(); - let dot = to_hmd.dot(watch_normal); - - if dot < app.session.config.watch_view_angle_min { - watch.state.want_visible = false; - } else { - watch.state.want_visible = true; - - watch.state.alpha = (dot - app.session.config.watch_view_angle_min) - / (app.session.config.watch_view_angle_max - app.session.config.watch_view_angle_min); - watch.state.alpha += 0.1; - watch.state.alpha = watch.state.alpha.clamp(0., 1.); - } -} diff --git a/src/res/anchor.yaml b/src/res/anchor.yaml deleted file mode 100644 index 11a9d1a..0000000 --- a/src/res/anchor.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# looking to make changes? -# drop me in ~/.config/wlxoverlay/anchor.yaml -# - -width: 0.1 - -size: [200, 200] - -# +X: right, +Y: up, +Z: back -spawn_pos: [0, 0, -1] - -elements: - - type: Panel - rect: [98, 0, 4, 200] - corner_radius: 0 - bg_color: "#ffff00" - - - type: Panel - rect: [0, 98, 200, 4] - corner_radius: 0 - bg_color: "#ffff00" - - - type: Label - rect: [8, 90, 600, 70] - corner_radius: 0 - font_size: 18 - fg_color: "#ffff00" - source: Static - text: Center diff --git a/src/res/settings.yaml b/src/res/settings.yaml deleted file mode 100644 index c448359..0000000 --- a/src/res/settings.yaml +++ /dev/null @@ -1,670 +0,0 @@ -# looking to make changes? -# drop me in ~/.config/wlxoverlay/settings.yaml -# - -width: 0.3 - -size: [600, 700] - -# +X: right, +Y: up, +Z: back -spawn_pos: [0, -0.1, -0.5] - -elements: - - type: Panel - rect: [0, 0, 600, 800] - corner_radius: 8 - bg_color: "#1e2030" - - - type: Label - rect: [15, 35, 600, 70] - corner_radius: 6 - font_size: 24 - fg_color: "#cad3f5" - source: Static - text: Settings - - - type: Button - rect: [560, 0, 40, 40] - corner_radius: 8 - font_size: 16 - bg_color: "#ed8796" - fg_color: "#24273a" - text: X - click_down: - - type: Window - target: "settings" - action: Destroy - - - type: Panel - rect: [50, 53, 500, 1] - corner_radius: 6 - bg_color: "#6e738d" - - ####### Watch Section ####### - - - type: Label - rect: [15, 85, 570, 24] - corner_radius: 6 - font_size: 18 - fg_color: "#cad3f5" - source: Static - text: Watch - - - type: Panel - rect: [250, 105, 1, 100] - corner_radius: 6 - bg_color: "#6e738d" - - - type: Label - rect: [288, 105, 100, 24] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: Visibility - - - type: Button - rect: [270, 120, 100, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Hide" - click_down: - - type: Watch - action: Hide - - - type: Button - rect: [270, 170, 100, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Swap Hand" - click_down: - - type: Watch - action: SwitchHands - - - type: Panel - rect: [390, 105, 1, 100] - corner_radius: 6 - bg_color: "#6e738d" - - - type: Label - rect: [430, 105, 120, 24] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: Watch Fade - - - type: Button - rect: [410, 120, 140, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Cutoff Point" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - ViewAngle: {kind: "MaxOpacity", delta: 0.01} - scroll_down: - - type: Watch - action: - ViewAngle: {kind: "MaxOpacity", delta: -0.01} - - - type: Button - rect: [410, 170, 140, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Cutoff Strength" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - ViewAngle: {kind: "MinOpacity", delta: 0.01} - scroll_down: - - type: Watch - action: - ViewAngle: {kind: "MinOpacity", delta: -0.01} - - - type: Label - rect: [25, 140, 90, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: Rotation - - - type: Button - rect: [108, 120, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "X" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - Rotation: {axis: "X", delta: 0.25} - scroll_down: - - type: Watch - action: - Rotation: {axis: "X", delta: -0.25} - - - type: Button - rect: [153, 120, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Y" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - Rotation: {axis: "Y", delta: 0.25} - scroll_down: - - type: Watch - action: - Rotation: {axis: "Y", delta: -0.25} - - - type: Button - rect: [198, 120, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Z" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - Rotation: {axis: "Z", delta: 0.25} - scroll_down: - - type: Watch - action: - Rotation: {axis: "Z", delta: -0.25} - - - type: Label - rect: [25, 190, 90, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: Position - - - type: Button - rect: [108, 170, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "X" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - Position: {axis: "X", delta: 0.001} - scroll_down: - - type: Watch - action: - Position: {axis: "X", delta: -0.001} - - - type: Button - rect: [153, 170, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Y" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - Position: {axis: "Y", delta: 0.001} - scroll_down: - - type: Watch - action: - Position: {axis: "Y", delta: -0.001} - - - type: Button - rect: [198, 170, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Z" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: Watch - action: - Position: {axis: "Z", delta: 0.001} - scroll_down: - - type: Watch - action: - Position: {axis: "Z", delta: -0.001} - - - type: Panel - rect: [50, 220, 500, 1] - corner_radius: 6 - bg_color: "#6e738d" - - ####### Mirror Section ####### - - type: Label - rect: [15, 255, 570, 24] - corner_radius: 6 - font_size: 18 - fg_color: "#cad3f5" - source: Static - text: Mirrors - - - type: Label - rect: [25, 290, 30, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: M1 - - - type: Button - rect: [60, 270, 110, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "Show/Hide" - click_down: # ToggleVisible if exists, else create - - type: Overlay - target: M1 - action: ToggleVisible # only fires if overlay exists - - type: Window - target: M1 - action: ShowMirror # only fires if not exists - - - type: Button - rect: [185, 270, 60, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "Lock" - click_down: - - type: Overlay - target: M1 - action: ToggleInteraction - - - type: Button - rect: [258, 270, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#ed8796" - text: "X" - click_down: - - type: Window - target: M1 - action: Destroy - - - type: Label - rect: [25, 340, 30, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: M2 - - - type: Button - rect: [60, 320, 110, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "Show/Hide" - click_down: - - type: Overlay - target: M2 - action: ToggleVisible - - type: Window - target: M2 - action: ShowMirror - - - type: Button - rect: [185, 320, 60, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "Lock" - click_down: - - type: Overlay - target: M2 - action: ToggleInteraction - - - type: Button - rect: [258, 320, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#ed8796" - text: "X" - click_down: - - type: Window - target: M2 - action: Destroy - - - type: Label - rect: [25, 390, 30, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: M3 - - - type: Button - rect: [60, 370, 110, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "Show/Hide" - click_down: - - type: Overlay - target: M3 - action: ToggleVisible - - type: Window - target: M3 - action: ShowMirror - - - type: Button - rect: [185, 370, 60, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "Lock" - click_down: - - type: Overlay - target: M3 - action: ToggleInteraction - - - type: Button - rect: [258, 370, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#ed8796" - text: "X" - click_down: - - type: Window - target: M3 - action: Destroy - - - type: Panel - rect: [300, 240, 1, 200] - corner_radius: 6 - bg_color: "#6e738d" - - ####### Color Gain Section ####### - - - type: Label - rect: [325, 255, 90, 24] - corner_radius: 6 - font_size: 18 - fg_color: "#cad3f5" - source: Static - text: Color Gain - - - type: Label - rect: [470, 255, 90, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - source: Static - text: (SteamVR) - - - type: Button - rect: [330, 270, 60, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#cad3f5" - bg_color: "#494d64" - text: "All" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: ColorAdjust - channel: All - delta: 0.01 - scroll_down: - - type: ColorAdjust - channel: All - delta: -0.01 - - - type: Button - rect: [405, 270, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#e78284" - text: "R" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: ColorAdjust - channel: R - delta: 0.01 - scroll_down: - - type: ColorAdjust - channel: R - delta: -0.01 - - - type: Button - rect: [450, 270, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#a6d189" - text: "G" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: ColorAdjust - channel: G - delta: 0.01 - scroll_down: - - type: ColorAdjust - channel: G - delta: -0.01 - - - type: Button - rect: [495, 270, 30, 30] - corner_radius: 15 - font_size: 12 - fg_color: "#24273a" - bg_color: "#8caaee" - text: "B" - click_down: - - type: Toast - message: Use stick up/down while hovering the button! - scroll_up: - - type: ColorAdjust - channel: B - delta: 0.01 - scroll_down: - - type: ColorAdjust - channel: B - delta: -0.01 - - - type: Panel - rect: [325, 315, 225, 1] - corner_radius: 6 - bg_color: "#6e738d" - - ####### Playspace Section ####### - - - type: Label - rect: [325, 345, 90, 24] - corner_radius: 6 - font_size: 18 - fg_color: "#cad3f5" - source: Static - text: Playspace - - - type: Button - rect: [330, 360, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Fix Floor" - click_down: - - type: System - action: PlayspaceFixFloor - - type: Window - target: "settings" - action: Destroy - - - type: Button - rect: [330, 410, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Reset Offset" - click_down: - - type: System - action: PlayspaceResetOffset - - type: Window - target: "settings" - action: Destroy - - ####### Notifications Section ####### - - - type: Panel - rect: [50, 460, 500, 1] - corner_radius: 6 - bg_color: "#6e738d" - - - type: Label - rect: [325, 490, 90, 24] - corner_radius: 6 - font_size: 18 - fg_color: "#cad3f5" - source: Static - text: Notifications - - - type: Button - rect: [330, 505, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#e64553" - text: "Enabled" - click_down: - - type: System - action: ToggleNotifications - highlight: Notifications - - - type: Button - rect: [330, 555, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#e64553" - text: "Sound Enabled" - click_down: - - type: System - action: ToggleNotificationSounds - highlight: NotificationSounds - - ####### Behavior Section ####### - - type: Label - rect: [15, 490, 570, 24] - corner_radius: 6 - font_size: 18 - fg_color: "#cad3f5" - source: Static - text: Behavior - - - type: Button - rect: [30, 505, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#e64553" - text: "Auto-Realign" - click_down: - - type: System - action: ToggleAutoRealign - highlight: AutoRealign - - - type: Button - rect: [30, 555, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#e64553" - text: "Grab+Scroll Slide" - click_down: - - type: System - action: ToggleAllowSliding - highlight: AllowSliding - - ####### Footer Section ####### - - - type: Panel - rect: [50, 605, 500, 1] - corner_radius: 6 - bg_color: "#6e738d" - - - type: Button - rect: [330, 625, 220, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Save Config" - click_down: - - type: System - action: PersistConfig - - type: Toast - message: Settings saved successfully. - - - type: Button - rect: [30, 625, 250, 30] - corner_radius: 6 - font_size: 12 - fg_color: "#24273a" - bg_color: "#eed49f" - text: "Save Overlay Layout" - click_down: - - type: System - action: PersistLayout - - type: Toast - message: Saved. You will see this layout on next startup. - diff --git a/src/res/watch.yaml b/src/res/watch.yaml deleted file mode 100644 index ddeb73c..0000000 --- a/src/res/watch.yaml +++ /dev/null @@ -1,192 +0,0 @@ -# looking to make changes? -# drop me in ~/.config/wlxoverlay/watch.yaml -# - -width: 0.115 - -size: [400, 200] - -elements: - # background panel - - type: Panel - rect: [0, 30, 400, 130] - corner_radius: 20 - bg_color: "#24273a" - - - type: Button - rect: [2, 162, 26, 36] - corner_radius: 4 - font_size: 15 - bg_color: "#c6a0f6" - fg_color: "#24273a" - text: "C" - click_up: # destroy if exists, otherwise create - - type: Window - target: settings - action: ShowUi # only triggers if not exists - - type: Window - target: settings - action: Destroy # only triggers if exists since before current frame - - # Dashboard toggle button - - type: Button - rect: [32, 162, 48, 36] - corner_radius: 4 - font_size: 15 - bg_color: "#2288FF" - fg_color: "#24273a" - text: "Dash" - click_up: - - type: WayVR - action: ToggleDashboard - - # Keyboard button - - type: Button - rect: [84, 162, 48, 36] - corner_radius: 4 - font_size: 15 - fg_color: "#24273a" - bg_color: "#a6da95" - text: Kbd - click_up: - - type: Overlay - target: "kbd" - action: ToggleVisible - long_click_up: - - type: Overlay - target: "kbd" - action: Reset - right_up: - - type: Overlay - target: "kbd" - action: ToggleImmovable - middle_up: - - type: Overlay - target: "kbd" - action: ToggleInteraction - scroll_up: - - type: Overlay - target: "kbd" - action: - Opacity: { delta: 0.025 } - scroll_down: - - type: Overlay - target: "kbd" - action: - Opacity: { delta: -0.025 } - - # bottom row, of keyboard + overlays - - type: OverlayList - rect: [134, 160, 266, 40] - corner_radius: 4 - font_size: 15 - fg_color: "#cad3f5" - bg_color: "#1e2030" - layout: Horizontal - click_up: ToggleVisible - long_click_up: Reset - right_up: ToggleImmovable - middle_up: ToggleInteraction - scroll_up: - Opacity: { delta: 0.025 } - scroll_down: - Opacity: { delta: -0.025 } - - # local clock - - type: Label - rect: [19, 90, 200, 50] - corner_radius: 4 - font_size: 46 # Use 32 for 12-hour time - fg_color: "#cad3f5" - source: Clock - format: "%H:%M" # 23:59 - #format: "%I:%M %p" # 11:59 PM - - # local date - - type: Label - rect: [20, 117, 200, 20] - corner_radius: 4 - font_size: 14 - fg_color: "#cad3f5" - source: Clock - format: "%x" # local date representation - - # local day-of-week - - type: Label - rect: [20, 137, 200, 50] - corner_radius: 4 - font_size: 14 - fg_color: "#cad3f5" - source: Clock - format: "%A" # Tuesday - #format: "%a" # Tue - - # alt clock 1 - - type: Label - rect: [210, 90, 200, 50] - corner_radius: 4 - font_size: 24 # Use 18 for 12-hour time - fg_color: "#8bd5ca" - source: Clock - timezone: 0 - format: "%H:%M" # 23:59 - #format: "%I:%M %p" # 11:59 PM - - type: Label - rect: [210, 60, 200, 50] - corner_radius: 4 - font_size: 14 - fg_color: "#8bd5ca" - source: Timezone - timezone: 0 - - # alt clock 2 - - type: Label - rect: [210, 150, 200, 50] - corner_radius: 4 - font_size: 24 # Use 18 for 12-hour time - fg_color: "#b7bdf8" - source: Clock - timezone: 1 - format: "%H:%M" # 23:59 - #format: "%I:%M %p" # 11:59 PM - - type: Label - rect: [210, 120, 200, 50] - corner_radius: 4 - font_size: 14 - fg_color: "#b7bdf8" - source: Timezone - timezone: 1 - - # batteries - - type: BatteryList - rect: [0, 5, 400, 30] - corner_radius: 4 - font_size: 16 - fg_color: "#8bd5ca" - fg_color_low: "#B06060" - fg_color_charging: "#6080A0" - num_devices: 9 - layout: Horizontal - low_threshold: 33 - - # volume buttons - - type: Button - rect: [315, 52, 70, 32] - corner_radius: 4 - font_size: 13 - fg_color: "#cad3f5" - bg_color: "#5b6078" - text: "Vol +" - click_down: - - type: Exec - command: ["pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%"] - - type: Button - rect: [315, 116, 70, 32] - corner_radius: 4 - font_size: 13 - fg_color: "#cad3f5" - bg_color: "#5b6078" - text: "Vol -" - click_down: - - type: Exec - command: ["pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%"] diff --git a/src/shaders/mod.rs b/src/shaders/mod.rs deleted file mode 100644 index 4de46d1..0000000 --- a/src/shaders/mod.rs +++ /dev/null @@ -1,266 +0,0 @@ -pub mod vert_common { - vulkano_shaders::shader! { - ty: "vertex", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_pos; - layout (location = 1) in vec2 in_uv; - layout (location = 0) out vec2 out_uv; - - void main() { - out_uv = in_uv; - gl_Position = vec4(in_pos * 2. - 1., 0., 1.); - } - ", - } -} - -pub mod frag_color { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform ColorBlock { - uniform vec4 in_color; - uniform vec2 corner_radius; - }; - - void main() - { - out_color.r = corner_radius.r; - out_color = in_color; - - vec2 uv_circ = ((1. - corner_radius) - (abs(in_uv + vec2(-0.5)) * 2.))/corner_radius; - float dist = length(uv_circ); - - out_color.a = mix(out_color.a, 0., - float(dist > 1.) - * float(uv_circ.x < 0.) - * float(uv_circ.y < 0.)); - } - ", - } -} - -//layout (location = 1) in float corner_radius; -//out_color = in_color; -// Some equation that determines whether to keep the pixel -// Use Lerp not if -//out_color.a = 0; - -pub mod frag_glyph { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - - layout (set = 1, binding = 0) uniform ColorBlock { - uniform vec4 in_color; - }; - - void main() - { - float r = texture(in_texture, in_uv).r; - out_color = vec4(r,r,r,r) * in_color; - } - ", - } -} - -pub mod frag_sprite2 { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - layout (set = 1, binding = 0) uniform UniBlock { - uniform vec4 st; - uniform vec4 mul; - }; - - void main() - { - out_color = texture(in_texture, (in_uv * st.xy) + st.zw) * mul; - } - ", - } -} - -pub mod frag_sprite2_hl { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - layout (set = 1, binding = 0) uniform UniBlock { - uniform vec4 st; - uniform vec4 mul; - }; - - void main() - { - out_color = texture(in_texture, (in_uv * st.xy) + st.zw).a * mul; - } - ", - } -} - -pub mod frag_sprite { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - - void main() - { - out_color = texture(in_texture, in_uv); - } - ", - } -} - -pub mod frag_grid { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - void main() - { - float fade = max(1.0 - 2.0 * length(in_uv.xy + vec2(-0.5, -0.5)), 0.0); - float grid; - - if (fract(in_uv.x / 0.0005) < 0.01 || fract(in_uv.y / 0.0005) < 0.01) { - grid = 1.0; - } else { - grid = 0.0; - } - out_color = vec4(1.0, 1.0, 1.0, grid * fade); - } - ", - } -} - -pub mod frag_screen { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - layout (set = 1, binding = 0) uniform AlphaBlock { - uniform float alpha; - }; - - void main() - { - out_color = texture(in_texture, in_uv); - out_color.a = alpha; - } - ", - } -} - -pub mod frag_srgb { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - layout (set = 1, binding = 0) uniform AlphaBlock { - uniform float alpha; - }; - - - void main() - { - out_color = texture(in_texture, in_uv); - - bvec4 cutoff = lessThan(out_color, vec4(0.04045)); - vec4 higher = pow((out_color + vec4(0.055))/vec4(1.055), vec4(2.4)); - vec4 lower = out_color/vec4(12.92); - - out_color = mix(higher, lower, cutoff); - out_color.a *= alpha; - } - ", - } -} - -pub mod frag_swapchain { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform sampler2D in_texture; - layout (set = 1, binding = 0) uniform AlphaBlock { - uniform float alpha; - }; - - void main() - { - out_color = texture(in_texture, in_uv); - out_color.a *= alpha; - } - ", - } -} - -pub mod frag_line { - vulkano_shaders::shader! { - ty: "fragment", - src: r"#version 310 es - precision highp float; - - layout (location = 0) in vec2 in_uv; - layout (location = 0) out vec4 out_color; - - layout (set = 0, binding = 0) uniform ColorBlock { - uniform vec4 in_color; - uniform vec2 unused; - }; - - void main() - { - out_color = in_color; - } - ", - } -} diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index b593af2..0000000 --- a/src/state.rs +++ /dev/null @@ -1,276 +0,0 @@ -use anyhow::bail; -use glam::Affine3A; -use idmap::IdMap; -use rodio::{Decoder, OutputStream, OutputStreamHandle, Source}; -use serde::{Deserialize, Serialize}; -use smallvec::{smallvec, SmallVec}; -use std::{io::Cursor, sync::Arc}; -use vulkano::image::view::ImageView; - -#[cfg(feature = "wayvr")] -use { - crate::config_wayvr::{self, WayVRConfig}, - crate::overlays::wayvr::WayVRData, - std::{cell::RefCell, rc::Rc}, -}; - -#[cfg(feature = "osc")] -use crate::backend::osc::OscSender; - -use crate::{ - backend::{input::InputState, overlay::OverlayID, task::TaskContainer}, - config::{AStrMap, GeneralConfig}, - config_io, - graphics::WlxGraphics, - gui::font::FontCache, - hid::HidProvider, - overlays::toast::{DisplayMethod, ToastTopic}, - shaders::{ - frag_color, frag_glyph, frag_grid, frag_line, frag_screen, frag_sprite, frag_sprite2, - frag_sprite2_hl, frag_srgb, frag_swapchain, vert_common, - }, -}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum KeyboardFocus { - PhysicalScreen, - - #[allow(dead_code)] // Not available if "wayvr" feature is disabled - WayVR, // (for now without wayland window id data, it's handled internally), -} - -pub struct AppState { - pub fc: FontCache, - pub session: AppSession, - pub tasks: TaskContainer, - pub graphics: Arc, - pub input_state: InputState, - pub hid_provider: Box, - pub audio: AudioOutput, - pub screens: SmallVec<[ScreenMeta; 8]>, - pub anchor: Affine3A, - pub sprites: AStrMap>, - pub keyboard_focus: KeyboardFocus, - pub toast_sound: &'static [u8], - - #[cfg(feature = "osc")] - pub osc_sender: Option, - - #[cfg(feature = "wayvr")] - pub wayvr: Option>>, // Dynamically created if requested -} - -impl AppState { - pub fn from_graphics(graphics: Arc) -> anyhow::Result { - // insert shared resources - { - let Ok(mut shaders) = graphics.shared_shaders.write() else { - bail!("Failed to lock shared shaders"); - }; - - let shader = vert_common::load(graphics.device.clone())?; - shaders.insert("vert_common", shader); - - let shader = frag_color::load(graphics.device.clone())?; - shaders.insert("frag_color", shader); - - let shader = frag_line::load(graphics.device.clone())?; - shaders.insert("frag_line", shader); - - let shader = frag_srgb::load(graphics.device.clone())?; - shaders.insert("frag_srgb", shader); - - let shader = frag_glyph::load(graphics.device.clone())?; - shaders.insert("frag_glyph", shader); - - let shader = frag_grid::load(graphics.device.clone())?; - shaders.insert("frag_grid", shader); - - let shader = frag_sprite::load(graphics.device.clone())?; - shaders.insert("frag_sprite", shader); - - let shader = frag_sprite2::load(graphics.device.clone())?; - shaders.insert("frag_sprite2", shader); - - let shader = frag_sprite2_hl::load(graphics.device.clone())?; - shaders.insert("frag_sprite2_hl", shader); - - let shader = frag_screen::load(graphics.device.clone())?; - shaders.insert("frag_screen", shader); - - let shader = frag_swapchain::load(graphics.device.clone())?; - shaders.insert("frag_swapchain", shader); - } - - #[cfg(feature = "wayvr")] - let mut tasks = TaskContainer::new(); - - #[cfg(not(feature = "wayvr"))] - let tasks = TaskContainer::new(); - - let session = AppSession::load(); - - #[cfg(feature = "wayvr")] - let wayvr = session - .wayvr_config - .post_load(&session.config, &mut tasks)?; - - #[cfg(feature = "osc")] - let osc_sender = crate::backend::osc::OscSender::new(session.config.osc_out_port).ok(); - - let toast_sound_wav = Self::try_load_bytes( - &session.config.notification_sound, - include_bytes!("res/557297.wav"), - ); - - Ok(Self { - fc: FontCache::new(session.config.primary_font.clone())?, - session, - tasks, - graphics, - input_state: InputState::new(), - hid_provider: crate::hid::initialize(), - audio: AudioOutput::new(), - screens: smallvec![], - anchor: Affine3A::IDENTITY, - sprites: AStrMap::new(), - keyboard_focus: KeyboardFocus::PhysicalScreen, - toast_sound: toast_sound_wav, - - #[cfg(feature = "osc")] - osc_sender, - - #[cfg(feature = "wayvr")] - wayvr, - }) - } - - #[cfg(feature = "wayvr")] - #[allow(dead_code)] - pub fn get_wayvr(&mut self) -> anyhow::Result>> { - if let Some(wvr) = &self.wayvr { - Ok(wvr.clone()) - } else { - let wayvr = Rc::new(RefCell::new(WayVRData::new( - WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config)?, - )?)); - self.wayvr = Some(wayvr.clone()); - Ok(wayvr) - } - } - - pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] { - if path.is_empty() { - return fallback_data; - } - - let real_path = config_io::get_config_root().join(path); - - if std::fs::File::open(real_path.clone()).is_err() { - log::warn!("Could not open file at: {path}"); - return fallback_data; - } - - match std::fs::read(real_path) { - // Box is used here to work around `f`'s limited lifetime - Ok(f) => Box::leak(Box::new(f)).as_slice(), - Err(e) => { - log::warn!("Failed to read file at: {path}"); - log::warn!("{e:?}"); - fallback_data - } - } - } -} - -pub struct AppSession { - pub config: GeneralConfig, - - #[cfg(feature = "wayvr")] - pub wayvr_config: WayVRConfig, - - pub toast_topics: IdMap, -} - -impl AppSession { - pub fn load() -> Self { - let config_root_path = config_io::ConfigRoot::Generic.ensure_dir(); - log::info!("Config root path: {}", config_root_path.display()); - let config = GeneralConfig::load_from_disk(); - - let mut toast_topics = IdMap::new(); - toast_topics.insert(ToastTopic::System, DisplayMethod::Center); - toast_topics.insert(ToastTopic::DesktopNotification, DisplayMethod::Center); - toast_topics.insert(ToastTopic::XSNotification, DisplayMethod::Center); - - config.notification_topics.iter().for_each(|(k, v)| { - toast_topics.insert(*k, *v); - }); - - #[cfg(feature = "wayvr")] - let wayvr_config = config_wayvr::load_wayvr(); - - Self { - config, - #[cfg(feature = "wayvr")] - wayvr_config, - toast_topics, - } - } -} - -pub struct AudioOutput { - audio_stream: Option<(OutputStream, OutputStreamHandle)>, - first_try: bool, -} - -impl AudioOutput { - pub const fn new() -> Self { - Self { - audio_stream: None, - first_try: true, - } - } - - fn get_handle(&mut self) -> Option<&OutputStreamHandle> { - if self.audio_stream.is_none() && self.first_try { - self.first_try = false; - if let Ok((stream, handle)) = OutputStream::try_default() { - self.audio_stream = Some((stream, handle)); - } else { - log::error!("Failed to open audio stream. Audio will not work."); - return None; - } - } - self.audio_stream.as_ref().map(|(_, h)| h) - } - - pub fn play(&mut self, wav_bytes: &'static [u8]) { - let Some(handle) = self.get_handle() else { - return; - }; - let cursor = Cursor::new(wav_bytes); - let source = match Decoder::new_wav(cursor) { - Ok(source) => source, - Err(e) => { - log::error!("Failed to play sound: {e:?}"); - return; - } - }; - let _ = handle.play_raw(source.convert_samples()); - } -} - -pub struct ScreenMeta { - pub name: Arc, - pub id: OverlayID, - pub native_handle: u32, -} - -#[derive(Serialize, Deserialize, Clone, Copy, Default)] -#[repr(u8)] -pub enum LeftRight { - #[default] - Left, - Right, -} diff --git a/uidev/.editorconfig b/uidev/.editorconfig new file mode 100644 index 0000000..f5834b7 --- /dev/null +++ b/uidev/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*.rs] +indent_style = tab +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/uidev/.gitignore b/uidev/.gitignore new file mode 100644 index 0000000..7be846b --- /dev/null +++ b/uidev/.gitignore @@ -0,0 +1,2 @@ +target +.vscode \ No newline at end of file diff --git a/uidev/Cargo.lock b/uidev/Cargo.lock new file mode 100644 index 0000000..3af3b4c --- /dev/null +++ b/uidev/Cargo.lock @@ -0,0 +1,3389 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +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 = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.9.1", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.9.1", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[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.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cosmic-text" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" +dependencies = [ + "bitflags 2.9.1", + "fontdb", + "log", + "rangemap", + "rustc-hash 1.1.0", + "rustybuzz", + "self_cell", + "smol_str", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "font-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "glam" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" + +[[package]] +name = "grid" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969d090eea77fac1be33f853ffc4b6b60cc6f312ddf4fd28b311a730e1dd8ebe" + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "gif", + "image-webp", + "num-traits", + "png", + "ravif", + "rayon", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kurbo" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" +dependencies = [ + "cfg-if", + "windows-targets 0.53.0", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.1", + "libc", + "redox_syscall 0.5.12", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.9.1", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[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 = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.9.1", + "block2", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-contacts", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.1", + "block2", + "dispatch", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-metal 0.3.1", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2 0.5.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.2.2", + "objc2-link-presentation", + "objc2-quartz-core 0.2.2", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.9.1", + "block2", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "orbclient" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owned_ttf_parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser 0.25.1", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.12", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce8e2ca6b24313587a03ca61bb74c384e2a815bd90cf2866cfc9f5fb7a11fa0" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rust-embed" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "self_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shaderc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e07913ada18607bb60d12431cbe3358d3bbebbe95948e1618851dc01e63b7b" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7" +dependencies = [ + "cmake", + "libc", + "roxmltree 0.14.1", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "skrifa" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe6666ab11018ab91ff7b03f1a3b9fdbecfb610848436fefa5ce50343d3d913" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slabbin" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db491c0d4152a069911a0fbdaca959691bf0b9d7110d98a7ed1c8e59b79ab30" + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.9.1", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dce3f0af95643c855cdc449fbaa17d8c2cd08e0b00a49a6babcbe6e71667f3d" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "taffy" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592835b1f82a500b55aa6242e5e2d1fb90c50e701fa9c33b5962d4aeaa4e88d6" +dependencies = [ + "arrayvec", + "grid", + "serde", + "slotmap", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uidev-vk" +version = "0.1.0" +dependencies = [ + "anyhow", + "glam", + "log", + "rust-embed", + "tracing-subscriber", + "wgui", + "winit", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64", + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "xmlwriter", +] + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vk-parse" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3859da4d7b98bec73e68fb65815d47a263819c415c90eed42b80440a02cbce8c" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "vulkano" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08840c2b51759a6f88f26f5ea378bc8b5c199a5b4760ddda292304be087249c4" +dependencies = [ + "ash", + "bytemuck", + "crossbeam-queue", + "foldhash", + "half", + "heck 0.4.1", + "indexmap", + "libloading", + "nom", + "once_cell", + "parking_lot", + "proc-macro2", + "quote", + "raw-window-handle", + "raw-window-metal", + "serde", + "serde_json", + "slabbin", + "smallvec", + "thread_local", + "vk-parse", + "vulkano-macros", +] + +[[package]] +name = "vulkano-macros" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc929c42c9336fd082079ac3ea30126e4a0dfe36fd2e2b3581303f7d140d20f" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "vulkano-shaders" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf501461be7cef2893c0e62c50945add9763cc482051d29053f6157089d5ea9" +dependencies = [ + "foldhash", + "heck 0.4.1", + "proc-macro2", + "quote", + "shaderc", + "syn", + "vulkano", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-backend" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +dependencies = [ + "bitflags 2.9.1", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.9.1", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + +[[package]] +name = "wgui" +version = "0.1.0" +dependencies = [ + "anyhow", + "cosmic-text", + "etagere", + "glam", + "image", + "log", + "lru", + "resvg", + "roxmltree 0.20.0", + "rustc-hash 2.1.1", + "slotmap", + "smallvec", + "taffy", + "vulkano", + "vulkano-shaders", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winit" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.9.1", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.9.1", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/uidev/Cargo.toml b/uidev/Cargo.toml new file mode 100644 index 0000000..b63d260 --- /dev/null +++ b/uidev/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "uidev" +version = "0.1.0" +edition = "2024" +description = "wgui library testing using winit backend" + +[dependencies] +anyhow = { workspace = true } +glam = { workspace = true } +log = { workspace = true } +rust-embed = "8.9.0" +tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } +wgui = { path = "../wgui/" } +winit = "0.30.12" +vulkano = { workspace = true } +vulkano-shaders = { workspace = true } +dash-frontend = { path = "../dash-frontend/" } diff --git a/uidev/assets/bar/add.svg b/uidev/assets/bar/add.svg new file mode 120000 index 0000000..d26bb19 --- /dev/null +++ b/uidev/assets/bar/add.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/add.svg \ No newline at end of file diff --git a/uidev/assets/bar/background-off.svg b/uidev/assets/bar/background-off.svg new file mode 120000 index 0000000..6c79183 --- /dev/null +++ b/uidev/assets/bar/background-off.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/background-off.svg \ No newline at end of file diff --git a/uidev/assets/bar/background.svg b/uidev/assets/bar/background.svg new file mode 120000 index 0000000..47931e4 --- /dev/null +++ b/uidev/assets/bar/background.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/background.svg \ No newline at end of file diff --git a/uidev/assets/bar/cancel.svg b/uidev/assets/bar/cancel.svg new file mode 120000 index 0000000..cf4283c --- /dev/null +++ b/uidev/assets/bar/cancel.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/cancel.svg \ No newline at end of file diff --git a/uidev/assets/bar/checkbox-checked.svg b/uidev/assets/bar/checkbox-checked.svg new file mode 120000 index 0000000..8db144a --- /dev/null +++ b/uidev/assets/bar/checkbox-checked.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/checkbox-checked.svg \ No newline at end of file diff --git a/uidev/assets/bar/checkbox.svg b/uidev/assets/bar/checkbox.svg new file mode 120000 index 0000000..db92a46 --- /dev/null +++ b/uidev/assets/bar/checkbox.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/checkbox.svg \ No newline at end of file diff --git a/uidev/assets/bar/delete.svg b/uidev/assets/bar/delete.svg new file mode 120000 index 0000000..cfd57cf --- /dev/null +++ b/uidev/assets/bar/delete.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/delete.svg \ No newline at end of file diff --git a/uidev/assets/bar/fade.svg b/uidev/assets/bar/fade.svg new file mode 120000 index 0000000..26f5a0c --- /dev/null +++ b/uidev/assets/bar/fade.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/fade.svg \ No newline at end of file diff --git a/uidev/assets/bar/inout.svg b/uidev/assets/bar/inout.svg new file mode 120000 index 0000000..6bcdc08 --- /dev/null +++ b/uidev/assets/bar/inout.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/inout.svg \ No newline at end of file diff --git a/uidev/assets/bar/lock.svg b/uidev/assets/bar/lock.svg new file mode 120000 index 0000000..b904120 --- /dev/null +++ b/uidev/assets/bar/lock.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/lock.svg \ No newline at end of file diff --git a/uidev/assets/bar/lock_open.svg b/uidev/assets/bar/lock_open.svg new file mode 120000 index 0000000..36aa8df --- /dev/null +++ b/uidev/assets/bar/lock_open.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/lock_open.svg \ No newline at end of file diff --git a/uidev/assets/bar/mouse.svg b/uidev/assets/bar/mouse.svg new file mode 120000 index 0000000..181f61d --- /dev/null +++ b/uidev/assets/bar/mouse.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/mouse.svg \ No newline at end of file diff --git a/uidev/assets/bar/mouse_lock.svg b/uidev/assets/bar/mouse_lock.svg new file mode 120000 index 0000000..658174f --- /dev/null +++ b/uidev/assets/bar/mouse_lock.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/mouse_lock.svg \ No newline at end of file diff --git a/uidev/assets/bar/move-all.svg b/uidev/assets/bar/move-all.svg new file mode 120000 index 0000000..dabd956 --- /dev/null +++ b/uidev/assets/bar/move-all.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/move-all.svg \ No newline at end of file diff --git a/uidev/assets/bar/move-horizontal.svg b/uidev/assets/bar/move-horizontal.svg new file mode 120000 index 0000000..df1c928 --- /dev/null +++ b/uidev/assets/bar/move-horizontal.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/move-horizontal.svg \ No newline at end of file diff --git a/uidev/assets/bar/resize.svg b/uidev/assets/bar/resize.svg new file mode 120000 index 0000000..7fe64c3 --- /dev/null +++ b/uidev/assets/bar/resize.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/resize.svg \ No newline at end of file diff --git a/uidev/assets/bar/screen-add.svg b/uidev/assets/bar/screen-add.svg new file mode 120000 index 0000000..6a16d93 --- /dev/null +++ b/uidev/assets/bar/screen-add.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/screen-add.svg \ No newline at end of file diff --git a/uidev/assets/bar/screen-options.svg b/uidev/assets/bar/screen-options.svg new file mode 120000 index 0000000..dfd9078 --- /dev/null +++ b/uidev/assets/bar/screen-options.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/screen-options.svg \ No newline at end of file diff --git a/uidev/assets/bar/screen-remove.svg b/uidev/assets/bar/screen-remove.svg new file mode 120000 index 0000000..5efeec9 --- /dev/null +++ b/uidev/assets/bar/screen-remove.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/bar/screen-remove.svg \ No newline at end of file diff --git a/uidev/assets/edit/add.svg b/uidev/assets/edit/add.svg new file mode 120000 index 0000000..f3fdd37 --- /dev/null +++ b/uidev/assets/edit/add.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/add.svg \ No newline at end of file diff --git a/uidev/assets/edit/anchor.svg b/uidev/assets/edit/anchor.svg new file mode 120000 index 0000000..2ef8762 --- /dev/null +++ b/uidev/assets/edit/anchor.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/anchor.svg \ No newline at end of file diff --git a/uidev/assets/edit/background-off.svg b/uidev/assets/edit/background-off.svg new file mode 120000 index 0000000..7f6c9a6 --- /dev/null +++ b/uidev/assets/edit/background-off.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/background-off.svg \ No newline at end of file diff --git a/uidev/assets/edit/background.svg b/uidev/assets/edit/background.svg new file mode 120000 index 0000000..cc614cc --- /dev/null +++ b/uidev/assets/edit/background.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/background.svg \ No newline at end of file diff --git a/uidev/assets/edit/cancel.svg b/uidev/assets/edit/cancel.svg new file mode 120000 index 0000000..5ff20ee --- /dev/null +++ b/uidev/assets/edit/cancel.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/cancel.svg \ No newline at end of file diff --git a/uidev/assets/edit/checkbox-checked.svg b/uidev/assets/edit/checkbox-checked.svg new file mode 120000 index 0000000..7795553 --- /dev/null +++ b/uidev/assets/edit/checkbox-checked.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/checkbox-checked.svg \ No newline at end of file diff --git a/uidev/assets/edit/checkbox.svg b/uidev/assets/edit/checkbox.svg new file mode 120000 index 0000000..8862964 --- /dev/null +++ b/uidev/assets/edit/checkbox.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/checkbox.svg \ No newline at end of file diff --git a/uidev/assets/edit/curve.svg b/uidev/assets/edit/curve.svg new file mode 120000 index 0000000..158e572 --- /dev/null +++ b/uidev/assets/edit/curve.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/curve.svg \ No newline at end of file diff --git a/uidev/assets/edit/delete.svg b/uidev/assets/edit/delete.svg new file mode 120000 index 0000000..316cee4 --- /dev/null +++ b/uidev/assets/edit/delete.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/delete.svg \ No newline at end of file diff --git a/uidev/assets/edit/desktop.svg b/uidev/assets/edit/desktop.svg new file mode 120000 index 0000000..6779bfa --- /dev/null +++ b/uidev/assets/edit/desktop.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/desktop.svg \ No newline at end of file diff --git a/uidev/assets/edit/fade.svg b/uidev/assets/edit/fade.svg new file mode 120000 index 0000000..fba0c68 --- /dev/null +++ b/uidev/assets/edit/fade.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/fade.svg \ No newline at end of file diff --git a/uidev/assets/edit/float.svg b/uidev/assets/edit/float.svg new file mode 120000 index 0000000..daea741 --- /dev/null +++ b/uidev/assets/edit/float.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/float.svg \ No newline at end of file diff --git a/uidev/assets/edit/grid.svg b/uidev/assets/edit/grid.svg new file mode 120000 index 0000000..699353b --- /dev/null +++ b/uidev/assets/edit/grid.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/grid.svg \ No newline at end of file diff --git a/uidev/assets/edit/inout.svg b/uidev/assets/edit/inout.svg new file mode 120000 index 0000000..76c26a9 --- /dev/null +++ b/uidev/assets/edit/inout.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/inout.svg \ No newline at end of file diff --git a/uidev/assets/edit/lock.svg b/uidev/assets/edit/lock.svg new file mode 120000 index 0000000..463ac64 --- /dev/null +++ b/uidev/assets/edit/lock.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/lock.svg \ No newline at end of file diff --git a/uidev/assets/edit/lock_open.svg b/uidev/assets/edit/lock_open.svg new file mode 120000 index 0000000..f27700f --- /dev/null +++ b/uidev/assets/edit/lock_open.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/lock_open.svg \ No newline at end of file diff --git a/uidev/assets/edit/mirror.svg b/uidev/assets/edit/mirror.svg new file mode 120000 index 0000000..f244788 --- /dev/null +++ b/uidev/assets/edit/mirror.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/mirror.svg \ No newline at end of file diff --git a/uidev/assets/edit/monitor.svg b/uidev/assets/edit/monitor.svg new file mode 120000 index 0000000..91ee221 --- /dev/null +++ b/uidev/assets/edit/monitor.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/monitor.svg \ No newline at end of file diff --git a/uidev/assets/edit/mouse.svg b/uidev/assets/edit/mouse.svg new file mode 120000 index 0000000..dd5a9db --- /dev/null +++ b/uidev/assets/edit/mouse.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/mouse.svg \ No newline at end of file diff --git a/uidev/assets/edit/mouse_lock.svg b/uidev/assets/edit/mouse_lock.svg new file mode 120000 index 0000000..5b2d15a --- /dev/null +++ b/uidev/assets/edit/mouse_lock.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/mouse_lock.svg \ No newline at end of file diff --git a/uidev/assets/edit/move-all.svg b/uidev/assets/edit/move-all.svg new file mode 120000 index 0000000..2d23158 --- /dev/null +++ b/uidev/assets/edit/move-all.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/move-all.svg \ No newline at end of file diff --git a/uidev/assets/edit/move-horizontal.svg b/uidev/assets/edit/move-horizontal.svg new file mode 120000 index 0000000..bda318a --- /dev/null +++ b/uidev/assets/edit/move-horizontal.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/move-horizontal.svg \ No newline at end of file diff --git a/uidev/assets/edit/pin.svg b/uidev/assets/edit/pin.svg new file mode 120000 index 0000000..a4960f4 --- /dev/null +++ b/uidev/assets/edit/pin.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/pin.svg \ No newline at end of file diff --git a/uidev/assets/edit/resize.svg b/uidev/assets/edit/resize.svg new file mode 120000 index 0000000..ffde6e0 --- /dev/null +++ b/uidev/assets/edit/resize.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/resize.svg \ No newline at end of file diff --git a/uidev/assets/edit/screen-add.svg b/uidev/assets/edit/screen-add.svg new file mode 120000 index 0000000..8a83ef5 --- /dev/null +++ b/uidev/assets/edit/screen-add.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/screen-add.svg \ No newline at end of file diff --git a/uidev/assets/edit/screen-options.svg b/uidev/assets/edit/screen-options.svg new file mode 120000 index 0000000..b4327b5 --- /dev/null +++ b/uidev/assets/edit/screen-options.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/screen-options.svg \ No newline at end of file diff --git a/uidev/assets/edit/screen-remove.svg b/uidev/assets/edit/screen-remove.svg new file mode 120000 index 0000000..e7e9b5a --- /dev/null +++ b/uidev/assets/edit/screen-remove.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/screen-remove.svg \ No newline at end of file diff --git a/uidev/assets/edit/window.svg b/uidev/assets/edit/window.svg new file mode 120000 index 0000000..4e61941 --- /dev/null +++ b/uidev/assets/edit/window.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/edit/window.svg \ No newline at end of file diff --git a/uidev/assets/gui/anchor.xml b/uidev/assets/gui/anchor.xml new file mode 100644 index 0000000..adc0c31 --- /dev/null +++ b/uidev/assets/gui/anchor.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/uidev/assets/gui/bar.xml b/uidev/assets/gui/bar.xml new file mode 120000 index 0000000..b4fb52e --- /dev/null +++ b/uidev/assets/gui/bar.xml @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/gui/bar.xml \ No newline at end of file diff --git a/uidev/assets/gui/testbed.xml b/uidev/assets/gui/testbed.xml new file mode 100644 index 0000000..67ecc67 --- /dev/null +++ b/uidev/assets/gui/testbed.xml @@ -0,0 +1,96 @@ + + + + + + + + + +
+
+ +
+ + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/uidev/assets/gui/testbed/icons.xml b/uidev/assets/gui/testbed/icons.xml new file mode 100644 index 0000000..14de271 --- /dev/null +++ b/uidev/assets/gui/testbed/icons.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/uidev/assets/gui/various_widgets.xml b/uidev/assets/gui/various_widgets.xml new file mode 100644 index 0000000..65cefb4 --- /dev/null +++ b/uidev/assets/gui/various_widgets.xml @@ -0,0 +1,102 @@ + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+
+
\ No newline at end of file diff --git a/uidev/assets/gui/watch.xml b/uidev/assets/gui/watch.xml new file mode 120000 index 0000000..9135c1c --- /dev/null +++ b/uidev/assets/gui/watch.xml @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/gui/watch.xml \ No newline at end of file diff --git a/uidev/assets/gui/wlx_theme.xml b/uidev/assets/gui/wlx_theme.xml new file mode 100644 index 0000000..7f0b20d --- /dev/null +++ b/uidev/assets/gui/wlx_theme.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/uidev/assets/lang/de.json b/uidev/assets/lang/de.json new file mode 100644 index 0000000..49ab324 --- /dev/null +++ b/uidev/assets/lang/de.json @@ -0,0 +1,9 @@ +{ + "BAR": { + "OPACITY": "Undurchsichtigkeit", + "ADDITIVE": "Additiv" + }, + "TESTBED": { + "HELLO_WORLD": "Hallo, Welt! Dies ist ein übersetzter I18n-Text." + } +} \ No newline at end of file diff --git a/uidev/assets/lang/en.json b/uidev/assets/lang/en.json new file mode 100644 index 0000000..a4c063b --- /dev/null +++ b/uidev/assets/lang/en.json @@ -0,0 +1,9 @@ +{ + "BAR": { + "OPACITY": "Opacity", + "ADDITIVE": "Additive" + }, + "TESTBED": { + "HELLO_WORLD": "Hello, world! This is a translated I18n text." + } +} diff --git a/uidev/assets/lang/es.json b/uidev/assets/lang/es.json new file mode 100644 index 0000000..1b88a15 --- /dev/null +++ b/uidev/assets/lang/es.json @@ -0,0 +1,9 @@ +{ + "BAR": { + "OPACITY": "Opacidad", + "ADDITIVE": "Aditivo" + }, + "TESTBED": { + "HELLO_WORLD": "¡Hola, mundo! Este es un texto traducido de I18n." + } +} \ No newline at end of file diff --git a/uidev/assets/lang/ja.json b/uidev/assets/lang/ja.json new file mode 100644 index 0000000..8fe0e3d --- /dev/null +++ b/uidev/assets/lang/ja.json @@ -0,0 +1,9 @@ +{ + "BAR": { + "OPACITY": "不透明度", + "ADDITIVE": "加法" + }, + "TESTBED": { + "HELLO_WORLD": "こんにちは、世界!これは翻訳されたI18nテキストです。" + } +} \ No newline at end of file diff --git a/uidev/assets/lang/pl.json b/uidev/assets/lang/pl.json new file mode 100644 index 0000000..4e553c8 --- /dev/null +++ b/uidev/assets/lang/pl.json @@ -0,0 +1,9 @@ +{ + "BAR": { + "OPACITY": "Przezroczystość", + "ADDITIVE": "Addytywny" + }, + "TESTBED": { + "HELLO_WORLD": "Witaj, świecie! To przetłumaczony tekst I18n." + } +} \ No newline at end of file diff --git a/uidev/assets/raster.png b/uidev/assets/raster.png new file mode 100644 index 0000000..32a5f5f Binary files /dev/null and b/uidev/assets/raster.png differ diff --git a/uidev/assets/watch/controller_l.svg b/uidev/assets/watch/controller_l.svg new file mode 120000 index 0000000..c77227e --- /dev/null +++ b/uidev/assets/watch/controller_l.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/controller_l.svg \ No newline at end of file diff --git a/uidev/assets/watch/controller_r.svg b/uidev/assets/watch/controller_r.svg new file mode 120000 index 0000000..3438418 --- /dev/null +++ b/uidev/assets/watch/controller_r.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/controller_r.svg \ No newline at end of file diff --git a/uidev/assets/watch/edit.svg b/uidev/assets/watch/edit.svg new file mode 120000 index 0000000..9955b79 --- /dev/null +++ b/uidev/assets/watch/edit.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/edit.svg \ No newline at end of file diff --git a/uidev/assets/watch/fix-floor.svg b/uidev/assets/watch/fix-floor.svg new file mode 120000 index 0000000..ac0b9a1 --- /dev/null +++ b/uidev/assets/watch/fix-floor.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/fix-floor.svg \ No newline at end of file diff --git a/uidev/assets/watch/hmd.svg b/uidev/assets/watch/hmd.svg new file mode 120000 index 0000000..fabe502 --- /dev/null +++ b/uidev/assets/watch/hmd.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/hmd.svg \ No newline at end of file diff --git a/uidev/assets/watch/home.svg b/uidev/assets/watch/home.svg new file mode 120000 index 0000000..f27eb01 --- /dev/null +++ b/uidev/assets/watch/home.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/home.svg \ No newline at end of file diff --git a/uidev/assets/watch/keyboard.svg b/uidev/assets/watch/keyboard.svg new file mode 120000 index 0000000..acfa4fb --- /dev/null +++ b/uidev/assets/watch/keyboard.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/keyboard.svg \ No newline at end of file diff --git a/uidev/assets/watch/mirror-off.svg b/uidev/assets/watch/mirror-off.svg new file mode 120000 index 0000000..d27dbd5 --- /dev/null +++ b/uidev/assets/watch/mirror-off.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/mirror-off.svg \ No newline at end of file diff --git a/uidev/assets/watch/recenter.svg b/uidev/assets/watch/recenter.svg new file mode 120000 index 0000000..063f53f --- /dev/null +++ b/uidev/assets/watch/recenter.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/recenter.svg \ No newline at end of file diff --git a/uidev/assets/watch/restart.svg b/uidev/assets/watch/restart.svg new file mode 120000 index 0000000..487ddd3 --- /dev/null +++ b/uidev/assets/watch/restart.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/restart.svg \ No newline at end of file diff --git a/uidev/assets/watch/set2.svg b/uidev/assets/watch/set2.svg new file mode 120000 index 0000000..72ed831 --- /dev/null +++ b/uidev/assets/watch/set2.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/set2.svg \ No newline at end of file diff --git a/uidev/assets/watch/settings.svg b/uidev/assets/watch/settings.svg new file mode 120000 index 0000000..7cb5bcd --- /dev/null +++ b/uidev/assets/watch/settings.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/settings.svg \ No newline at end of file diff --git a/uidev/assets/watch/track.svg b/uidev/assets/watch/track.svg new file mode 120000 index 0000000..15660af --- /dev/null +++ b/uidev/assets/watch/track.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/track.svg \ No newline at end of file diff --git a/uidev/assets/watch/track2.svg b/uidev/assets/watch/track2.svg new file mode 120000 index 0000000..eb98b02 --- /dev/null +++ b/uidev/assets/watch/track2.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/track2.svg \ No newline at end of file diff --git a/uidev/assets/watch/track3.svg b/uidev/assets/watch/track3.svg new file mode 120000 index 0000000..3a88878 --- /dev/null +++ b/uidev/assets/watch/track3.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/watch/track3.svg \ No newline at end of file diff --git a/uidev/res/watch.xml b/uidev/res/watch.xml new file mode 100644 index 0000000..e69de29 diff --git a/uidev/rustfmt.toml b/uidev/rustfmt.toml new file mode 100644 index 0000000..39694dc --- /dev/null +++ b/uidev/rustfmt.toml @@ -0,0 +1,2 @@ +tab_spaces = 2 +hard_tabs = true \ No newline at end of file diff --git a/uidev/src/assets.rs b/uidev/src/assets.rs new file mode 100644 index 0000000..cbc6b5e --- /dev/null +++ b/uidev/src/assets.rs @@ -0,0 +1,12 @@ +#[derive(rust_embed::Embed)] +#[folder = "assets/"] +pub struct Asset; + +impl wgui::assets::AssetProvider for Asset { + fn load_from_path(&mut self, path: &str) -> anyhow::Result> { + match Asset::get(path) { + Some(data) => Ok(data.data.to_vec()), + None => anyhow::bail!("embedded file {} not found", path), + } + } +} diff --git a/uidev/src/main.rs b/uidev/src/main.rs new file mode 100644 index 0000000..5c3593b --- /dev/null +++ b/uidev/src/main.rs @@ -0,0 +1,456 @@ +use glam::{Vec2, vec2}; +use std::sync::Arc; +use testbed::{Testbed, testbed_any::TestbedAny}; +use timestep::Timestep; +use tracing_subscriber::EnvFilter; +use tracing_subscriber::filter::LevelFilter; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; +use vulkan::init_window; +use vulkano::{ + Validated, VulkanError, + command_buffer::CommandBufferUsage, + format::Format, + image::{ImageUsage, view::ImageView}, + swapchain::{ + CompositeAlpha, PresentMode, Surface, SurfaceInfo, Swapchain, SwapchainCreateInfo, + SwapchainPresentInfo, acquire_next_image, + }, + sync::GpuFuture, +}; +use wgui::{ + event::{MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent}, + gfx::{WGfx, cmd::WGfxClearMode}, + renderer_vk::{self}, +}; +use winit::{ + event::{ElementState, Event, MouseScrollDelta, WindowEvent}, + event_loop::ControlFlow, + keyboard::{KeyCode, PhysicalKey}, +}; + +use crate::{ + rate_limiter::RateLimiter, + testbed::{ + TestbedUpdateParams, testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric, + }, +}; + +mod assets; +mod profiler; +mod rate_limiter; +mod testbed; +mod timestep; +mod vulkan; + +fn init_logging() { + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .pretty() + .with_writer(std::io::stderr), + ) + .with( + /* read RUST_LOG env var */ + EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env_lossy() + .add_directive("cosmic_text=info".parse().unwrap()), + ) + .init(); +} + +fn load_testbed() -> anyhow::Result> { + let name = std::env::var("TESTBED").unwrap_or_default(); + Ok(match name.as_str() { + "dashboard" => Box::new(TestbedDashboard::new()?), + "" => Box::new(TestbedGeneric::new()?), + _ => Box::new(TestbedAny::new(&name)?), + }) +} + +fn main() -> Result<(), Box> { + init_logging(); + + let (gfx, event_loop, window, surface) = + init_window("[-/=]: gui scale, F10: debug draw, F11: print tree")?; + let inner_size = window.inner_size(); + let mut swapchain_size = [inner_size.width, inner_size.height]; + + let mut swapchain_create_info = + swapchain_create_info(&gfx, gfx.surface_format, surface.clone(), swapchain_size); + + let (mut swapchain, mut images) = { + let (swapchain, images) = Swapchain::new( + gfx.device.clone(), + surface.clone(), + swapchain_create_info.clone(), + )?; + + let image_views = images + .into_iter() + .map(|image| ImageView::new_default(image).unwrap()) + .collect::>(); + + (swapchain, image_views) + }; + + let mut recreate = false; + + let mut scale = window.scale_factor() as f32; + + let mut testbed = load_testbed()?; + + let mut mouse = Vec2::ZERO; + + let mut shared_context = renderer_vk::context::SharedContext::new(gfx.clone())?; + let mut render_context = renderer_vk::context::Context::new(&mut shared_context, scale)?; + + render_context.update_viewport(&mut shared_context, swapchain_size, scale)?; + log::trace!("new swapchain_size: {swapchain_size:?}"); + + let mut debug_draw_enabled = false; + + let mut profiler = profiler::Profiler::new(1000); + let mut frame_index: u64 = 0; + + let mut timestep = Timestep::new(); + timestep.set_tps(60.0); + + let mut limiter = RateLimiter::new(); + + #[allow(deprecated)] + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Poll); + + match event { + Event::WindowEvent { + event: WindowEvent::MouseWheel { delta, .. }, + .. + } => match delta { + MouseScrollDelta::LineDelta(x, y) => { + testbed + .layout() + .borrow_mut() + .push_event( + &wgui::event::Event::MouseWheel(MouseWheelEvent { + delta: Vec2::new(x, y), + pos: mouse / scale, + device: 0, + }), + &mut (), + &mut (), + ) + .unwrap(); + } + MouseScrollDelta::PixelDelta(pos) => { + testbed + .layout() + .borrow_mut() + .push_event( + &wgui::event::Event::MouseWheel(MouseWheelEvent { + delta: Vec2::new(pos.x as f32 / 5.0, pos.y as f32 / 5.0), + pos: mouse / scale, + device: 0, + }), + &mut (), + &mut (), + ) + .unwrap(); + } + }, + Event::WindowEvent { + event: WindowEvent::MouseInput { state, button, .. }, + .. + } => { + if matches!(button, winit::event::MouseButton::Left) { + if matches!(state, winit::event::ElementState::Pressed) { + testbed + .layout() + .borrow_mut() + .push_event( + &wgui::event::Event::MouseDown(MouseDownEvent { + pos: mouse / scale, + index: MouseButtonIndex::Left, + device: 0, + }), + &mut (), + &mut (), + ) + .unwrap(); + } else { + testbed + .layout() + .borrow_mut() + .push_event( + &wgui::event::Event::MouseUp(MouseUpEvent { + pos: mouse / scale, + index: MouseButtonIndex::Left, + device: 0, + }), + &mut (), + &mut (), + ) + .unwrap(); + } + } + } + Event::WindowEvent { + event: WindowEvent::CursorMoved { position, .. }, + .. + } => { + mouse = vec2(position.x as _, position.y as _); + testbed + .layout() + .borrow_mut() + .push_event( + &wgui::event::Event::MouseMotion(MouseMotionEvent { + pos: mouse / scale, + device: 0, + }), + &mut (), + &mut (), + ) + .unwrap(); + } + Event::WindowEvent { + event: WindowEvent::KeyboardInput { event, .. }, + .. + } => { + if event.state == ElementState::Pressed { + if event.physical_key == PhysicalKey::Code(KeyCode::F10) { + debug_draw_enabled = !debug_draw_enabled; + if debug_draw_enabled { + log::info!( + "Debug draw enabled\n\tAqua: widget boundary\n\tMagenta: Scissoring (separate render pass)" + ); + } + testbed.layout().borrow_mut().mark_redraw(); + } + + if event.physical_key == PhysicalKey::Code(KeyCode::F11) { + testbed.layout().borrow_mut().print_tree(); + } + + if event.physical_key == PhysicalKey::Code(KeyCode::Equal) { + scale *= 1.25; + render_context + .update_viewport(&mut shared_context, swapchain_size, scale) + .unwrap(); + } + + if event.physical_key == PhysicalKey::Code(KeyCode::Minus) { + scale *= 0.75; + render_context + .update_viewport(&mut shared_context, swapchain_size, scale) + .unwrap(); + } + } + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + elwt.exit(); + } + Event::WindowEvent { + event: WindowEvent::Resized(_), + .. + } => { + recreate = true; + } + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + if recreate { + let inner_size = window.inner_size(); + swapchain_size = [inner_size.width, inner_size.height]; + + swapchain_create_info.image_extent = swapchain_size; + + (swapchain, images) = { + let (swapchain, images) = swapchain.recreate(swapchain_create_info.clone()).unwrap(); + + let image_views = images + .into_iter() + .map(|image| ImageView::new_default(image).unwrap()) + .collect::>(); + + (swapchain, image_views) + }; + + render_context + .update_viewport(&mut shared_context, swapchain_size, scale) + .unwrap(); + + log::trace!("new swapchain_size: {swapchain_size:?}"); + recreate = false; + window.request_redraw(); + } + + while timestep.on_tick() { + testbed.layout().borrow_mut().tick().unwrap(); + } + + testbed + .update(TestbedUpdateParams { + width: (swapchain_size[0] as f32 / scale) as _, + height: (swapchain_size[1] as f32 / scale) as _, + timestep_alpha: timestep.alpha, + }) + .unwrap(); + + if !render_context.dirty && !testbed.layout().borrow_mut().check_toggle_needs_redraw() { + // no need to redraw + std::thread::sleep(std::time::Duration::from_millis(5)); // dirty fix to prevent cpu burning precious cycles doing a busy loop + return; + } + + log::trace!("drawing frame {frame_index}"); + frame_index += 1; + + limiter.start(120); // max 120 fps + profiler.start(); + + { + let (image_index, _, acquire_future) = + match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + recreate = true; + return; + } + Err(e) => { + log::error!("failed to acquire next image: {e}"); + return; + } + }; + + let tgt = images[image_index as usize].clone(); + + let mut cmd_buf = gfx + .create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit) + .unwrap(); + cmd_buf + .begin_rendering(tgt, WGfxClearMode::Clear([0.0, 0.0, 0.0, 0.1])) + .unwrap(); + + let mut layout = testbed.layout().borrow_mut(); + let globals = layout.state.globals.clone(); + let mut globals = globals.get(); + + let mut draw_params = wgui::drawing::DrawParams { + globals: &mut globals, + layout: &mut layout, + debug_draw: debug_draw_enabled, + timestep_alpha: timestep.alpha, + }; + + let primitives = wgui::drawing::draw(&mut draw_params).unwrap(); + drop(layout); + + let draw_result = render_context + .draw( + &globals.font_system, + &mut shared_context, + &mut cmd_buf, + &primitives, + ) + .unwrap(); + + if debug_draw_enabled { + log::debug!("pass count: {}", draw_result.pass_count); + } + + cmd_buf.end_rendering().unwrap(); + + let cmd_buf = cmd_buf.build().unwrap(); + + acquire_future + .then_execute(gfx.queue_gfx.clone(), cmd_buf) + .unwrap() + .then_swapchain_present( + gfx.queue_gfx.clone(), + SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush() + .unwrap() + .wait(None) + .unwrap(); + } + + profiler.end(); + limiter.end(); + } + Event::AboutToWait => { + // should be limited to vsync + window.request_redraw(); + } + _ => (), + } + })?; + + Ok(()) +} + +fn swapchain_create_info( + graphics: &WGfx, + format: Format, + surface: Arc, + extent: [u32; 2], +) -> SwapchainCreateInfo { + let surface_capabilities = graphics + .device + .physical_device() + .surface_capabilities(&surface, SurfaceInfo::default()) + .unwrap(); // want panic + + let mut composite_alpha = None; + for c in [ + CompositeAlpha::PreMultiplied, + CompositeAlpha::PostMultiplied, + CompositeAlpha::Opaque, + ] { + if surface_capabilities + .supported_composite_alpha + .contains_enum(c) + { + composite_alpha = Some(c); + break; + } + log::warn!( + "GPU driver doesn't support {c:?} compositeAlpha! Desktop window will be blended using a fallback method and may look different than in VR." + ); + } + let composite_alpha = composite_alpha + .expect("GPU driver issue: VkSurfaceCapabilitiesKHR has empty supportedCompositeAlpha."); + + let present_modes = graphics + .device + .physical_device() + .surface_present_modes(&surface, SurfaceInfo::default()) + .expect("Could not get GPU present modes for VKSurface."); + + let mut present_mode = None; + for pm in [PresentMode::Mailbox, PresentMode::Fifo] { + if present_modes.contains(&pm) { + present_mode = Some(pm); + break; + } + log::warn!( + "GPU driver doesn't support {pm:?} presentMode! Desktop window may have a higher latency and/or glitch during grab/resize." + ); + } + let present_mode = present_mode.expect("GPU driver issue: VkPresentModeKHR FIFO is not supported even though it's required by Vulkan spec."); + + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + present_mode, + image_format: format, + image_extent: extent, + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha, + ..Default::default() + } +} diff --git a/uidev/src/profiler.rs b/uidev/src/profiler.rs new file mode 100644 index 0000000..f450310 --- /dev/null +++ b/uidev/src/profiler.rs @@ -0,0 +1,43 @@ +use crate::timestep::get_micros; + +pub struct Profiler { + interval_us: u64, + last_measure_us: u64, + frametime_sum_us: u64, + measure_frames: u64, + time_start_us: u64, +} + +impl Profiler { + pub fn new(interval_ms: u64) -> Self { + Self { + frametime_sum_us: 0, + interval_us: interval_ms * 1000, + last_measure_us: 0, + measure_frames: 0, + time_start_us: 0, + } + } + + pub fn start(&mut self) { + self.time_start_us = get_micros(); + } + + pub fn end(&mut self) { + let cur_micros = get_micros(); + + let frametime = cur_micros - self.time_start_us; + self.measure_frames += 1; + self.frametime_sum_us += frametime; + + if self.last_measure_us + self.interval_us < cur_micros { + log::trace!( + "avg frametime: {:.3}ms", + (self.frametime_sum_us / self.measure_frames) as f32 / 1000.0 + ); + self.last_measure_us = cur_micros; + self.frametime_sum_us = 0; + self.measure_frames = 0; + } + } +} diff --git a/uidev/src/rate_limiter.rs b/uidev/src/rate_limiter.rs new file mode 100644 index 0000000..4ad3186 --- /dev/null +++ b/uidev/src/rate_limiter.rs @@ -0,0 +1,37 @@ +use crate::timestep::get_micros; + +#[derive(Default)] +pub struct RateLimiter { + rate: u16, + start_us: u64, + end_us: u64, +} + +impl RateLimiter { + pub fn new() -> Self { + Self { + rate: 0, + end_us: 0, + start_us: 0, + } + } + + pub fn start(&mut self, rate: u16) { + self.rate = rate; + self.start_us = get_micros(); + } + + pub fn end(&mut self) { + if self.rate == 0 { + return; + } + + self.end_us = get_micros(); + let microseconds = self.end_us - self.start_us; + let frametime_microseconds = ((1000.0 / self.rate as f32) * 1000.0) as u64; + let delay = frametime_microseconds as i64 - microseconds as i64; + if delay > 0 { + std::thread::sleep(std::time::Duration::from_micros(delay as u64)); + } + } +} diff --git a/uidev/src/testbed/mod.rs b/uidev/src/testbed/mod.rs new file mode 100644 index 0000000..e1e0ac6 --- /dev/null +++ b/uidev/src/testbed/mod.rs @@ -0,0 +1,16 @@ +use wgui::layout::RcLayout; + +pub mod testbed_any; +pub mod testbed_dashboard; +pub mod testbed_generic; + +pub struct TestbedUpdateParams { + pub width: f32, + pub height: f32, + pub timestep_alpha: f32, +} + +pub trait Testbed { + fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()>; + fn layout(&self) -> &RcLayout; +} diff --git a/uidev/src/testbed/testbed_any.rs b/uidev/src/testbed/testbed_any.rs new file mode 100644 index 0000000..9c8ccae --- /dev/null +++ b/uidev/src/testbed/testbed_any.rs @@ -0,0 +1,65 @@ +use std::path::PathBuf; + +use crate::{ + assets, + testbed::{Testbed, TestbedUpdateParams}, +}; +use glam::Vec2; +use wgui::{ + assets::AssetPath, + font_config::WguiFontConfig, + globals::WguiGlobals, + layout::{LayoutParams, RcLayout}, + parser::{ParseDocumentParams, ParserState}, +}; + +pub struct TestbedAny { + pub layout: RcLayout, + + #[allow(dead_code)] + state: ParserState, +} + +impl TestbedAny { + pub fn new(name: &str) -> anyhow::Result { + let path = if name.ends_with(".xml") { + AssetPath::FileOrBuiltIn(name) + } else { + AssetPath::BuiltIn(&format!("gui/{name}.xml")) + }; + + let globals = WguiGlobals::new( + Box::new(assets::Asset {}), + wgui::globals::Defaults::default(), + &WguiFontConfig::default(), + PathBuf::new(), // cwd + )?; + + let (layout, state) = wgui::parser::new_layout_from_assets( + &ParseDocumentParams { + globals, + path, + extra: Default::default(), + }, + &LayoutParams::default(), + )?; + Ok(Self { + layout: layout.as_rc(), + state, + }) + } +} + +impl Testbed for TestbedAny { + fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> { + self.layout.borrow_mut().update( + Vec2::new(params.width, params.height), + params.timestep_alpha, + )?; + Ok(()) + } + + fn layout(&self) -> &RcLayout { + &self.layout + } +} diff --git a/uidev/src/testbed/testbed_dashboard.rs b/uidev/src/testbed/testbed_dashboard.rs new file mode 100644 index 0000000..3f8d7a4 --- /dev/null +++ b/uidev/src/testbed/testbed_dashboard.rs @@ -0,0 +1,84 @@ +use crate::testbed::{Testbed, TestbedUpdateParams}; +use dash_frontend::{ + frontend, + settings::{self, SettingsIO}, +}; +use wgui::layout::RcLayout; + +struct SimpleSettingsIO { + settings: settings::Settings, +} + +impl SimpleSettingsIO { + fn new() -> Self { + let mut res = Self { + settings: settings::Settings::default(), + }; + res.read_from_disk(); + res + } +} + +// just a simple impl of a config io for dashboard frontend +// use ~/.config later +impl settings::SettingsIO for SimpleSettingsIO { + fn get_mut(&mut self) -> &mut settings::Settings { + &mut self.settings + } + + fn get(&self) -> &dash_frontend::settings::Settings { + &self.settings + } + + fn save_to_disk(&mut self) { + log::info!("saving settings"); + let data = self.settings.save(); + std::fs::write("/tmp/testbed_settings.json", data).unwrap(); + } + + fn read_from_disk(&mut self) { + log::info!("loading settings"); + if let Ok(res) = std::fs::read("/tmp/testbed_settings.json") { + let data = String::from_utf8(res).unwrap(); + self.settings = settings::Settings::load(&data).unwrap(); + } + } + + fn mark_as_dirty(&mut self) { + // just save it, at least for now + // save_to_disk should be called later in time or at exit, not instantly + self.save_to_disk(); + } +} + +pub struct TestbedDashboard { + layout: RcLayout, + frontend: frontend::RcFrontend, +} + +impl TestbedDashboard { + pub fn new() -> anyhow::Result { + let settings = SimpleSettingsIO::new(); + + let (frontend, layout) = frontend::Frontend::new(frontend::InitParams { + settings: Box::new(settings), + })?; + Ok(Self { frontend, layout }) + } +} + +impl Testbed for TestbedDashboard { + fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> { + let mut frontend = self.frontend.borrow_mut(); + frontend.update( + &self.frontend, + params.width, + params.height, + params.timestep_alpha, + ) + } + + fn layout(&self) -> &RcLayout { + &self.layout + } +} diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs new file mode 100644 index 0000000..9d93d6a --- /dev/null +++ b/uidev/src/testbed/testbed_generic.rs @@ -0,0 +1,250 @@ +use std::{cell::RefCell, collections::VecDeque, path::PathBuf, rc::Rc}; + +use crate::{ + assets, + testbed::{Testbed, TestbedUpdateParams}, +}; +use glam::Vec2; +use wgui::{ + assets::AssetPath, + components::{ + Component, + button::{ButtonClickCallback, ComponentButton}, + checkbox::ComponentCheckbox, + }, + drawing::Color, + event::StyleSetRequest, + font_config::WguiFontConfig, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, LayoutParams, RcLayout, Widget}, + parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams, ParserState}, + taffy, + widget::{label::WidgetLabel, rectangle::WidgetRectangle}, + windowing::{WguiWindow, WguiWindowParams}, +}; + +pub enum TestbedTask { + ShowPopup, +} + +struct Data { + tasks: VecDeque, + #[allow(dead_code)] + state: ParserState, + + popup_window: WguiWindow, +} + +#[derive(Clone)] +pub struct TestbedGeneric { + pub layout: RcLayout, + + globals: WguiGlobals, + data: Rc>, +} + +fn button_click_callback( + button: Component, + label: Widget, + text: &'static str, +) -> ButtonClickCallback { + Box::new(move |common, _e| { + label + .get_as_mut::() + .unwrap() + .set_text(common, Translation::from_raw_text(text)); + + button.try_cast::()?.set_text( + common, + Translation::from_raw_text("this button has been clicked"), + ); + + Ok(()) + }) +} + +fn handle_button_click(button: Rc, label: Widget, text: &'static str) { + button.on_click(button_click_callback( + Component(button.clone()), + label, + text, + )); +} + +impl TestbedGeneric { + pub fn new() -> anyhow::Result { + const XML_PATH: AssetPath = AssetPath::BuiltIn("gui/various_widgets.xml"); + + let globals = WguiGlobals::new( + Box::new(assets::Asset {}), + wgui::globals::Defaults::default(), + &WguiFontConfig::default(), + PathBuf::new(), // cwd + )?; + + let extra = ParseDocumentExtra { + on_custom_attribs: Some(Box::new(move |par| { + let Some(my_custom_value) = par.get_value("_my_custom") else { + return; + }; + + let Some(mult_value) = par.get_value("_mult") else { + return; + }; + + let mult_f32 = mult_value.parse::().unwrap(); + + let mut color = match my_custom_value.as_ref() { + "red" => Color::new(1.0, 0.0, 0.0, 1.0), + "green" => Color::new(0.0, 1.0, 0.0, 1.0), + "blue" => Color::new(0.0, 0.0, 1.0, 1.0), + _ => Color::new(1.0, 1.0, 1.0, 1.0), + }; + + color = color.mult_rgb(mult_f32); + + let mut rect = par.get_widget_as::().unwrap(); + rect.params.color = color; + })), + dev_mode: false, + }; + + let (layout, state) = wgui::parser::new_layout_from_assets( + &ParseDocumentParams { + globals: globals.clone(), + path: XML_PATH, + extra, + }, + &LayoutParams { + resize_to_parent: true, + }, + )?; + + let cb_visible = state.fetch_component_as::("cb_visible")?; + let div_visibility = state.fetch_widget(&layout.state, "div_visibility")?; + + cb_visible.on_toggle(Box::new(move |common, evt| { + common.alterables.set_style( + div_visibility.id, + StyleSetRequest::Display(if evt.checked { + taffy::Display::Flex + } else { + taffy::Display::None + }), + ); + Ok(()) + })); + + let label_cur_option = state.fetch_widget(&layout.state, "label_current_option")?; + + let button_click_me = state.fetch_component_as::("button_click_me")?; + let button = button_click_me.clone(); + button_click_me.on_click(Box::new(move |common, _e| { + button.set_text(common, Translation::from_raw_text("congrats!")); + Ok(()) + })); + + let button_popup = state.fetch_component_as::("button_popup")?; + let button_red = state.fetch_component_as::("button_red")?; + let button_aqua = state.fetch_component_as::("button_aqua")?; + let button_yellow = state.fetch_component_as::("button_yellow")?; + + handle_button_click(button_red, label_cur_option.widget.clone(), "Clicked red"); + handle_button_click(button_aqua, label_cur_option.widget.clone(), "Clicked aqua"); + handle_button_click( + button_yellow, + label_cur_option.widget.clone(), + "Clicked yellow", + ); + + let cb_first = state.fetch_component_as::("cb_first")?; + let label = label_cur_option.widget.clone(); + cb_first.on_toggle(Box::new(move |common, e| { + let mut widget = label.get_as_mut::().unwrap(); + let text = format!("checkbox toggle: {}", e.checked); + widget.set_text(common, Translation::from_raw_text(&text)); + Ok(()) + })); + + let testbed = Self { + layout: layout.as_rc(), + globals: globals.clone(), + data: Rc::new(RefCell::new(Data { + state, + tasks: Default::default(), + popup_window: WguiWindow::default(), + })), + }; + + button_popup.on_click({ + let testbed = testbed.clone(); + Box::new(move |_, _| { + testbed.push_task(TestbedTask::ShowPopup); + Ok(()) + }) + }); + + Ok(testbed) + } + + fn push_task(&self, task: TestbedTask) { + self.data.borrow_mut().tasks.push_back(task); + } + + fn process_task( + &mut self, + task: &TestbedTask, + params: &mut TestbedUpdateParams, + layout: &mut Layout, + data: &mut Data, + ) -> anyhow::Result<()> { + match task { + TestbedTask::ShowPopup => self.show_popup(params, layout, data)?, + } + + Ok(()) + } + + fn show_popup( + &mut self, + _params: &mut TestbedUpdateParams, + layout: &mut Layout, + data: &mut Data, + ) -> anyhow::Result<()> { + data.popup_window.open(&mut WguiWindowParams { + globals: self.globals.clone(), + position: Vec2::new(128.0, 128.0), + layout, + title: Translation::from_raw_text("foo"), + extra: Default::default(), + })?; + + Ok(()) + } +} + +impl Testbed for TestbedGeneric { + fn update(&mut self, mut params: TestbedUpdateParams) -> anyhow::Result<()> { + let layout = self.layout.clone(); + let data = self.data.clone(); + + let mut layout = layout.borrow_mut(); + let mut data = data.borrow_mut(); + + layout.update( + Vec2::new(params.width, params.height), + params.timestep_alpha, + )?; + + while let Some(task) = data.tasks.pop_front() { + self.process_task(&task, &mut params, &mut layout, &mut data)?; + } + + Ok(()) + } + + fn layout(&self) -> &RcLayout { + &self.layout + } +} diff --git a/uidev/src/timestep.rs b/uidev/src/timestep.rs new file mode 100644 index 0000000..e0e1101 --- /dev/null +++ b/uidev/src/timestep.rs @@ -0,0 +1,70 @@ +use std::{sync::LazyLock, time::Instant}; +static TIME_START: LazyLock = LazyLock::new(Instant::now); + +pub fn get_micros() -> u64 { + TIME_START.elapsed().as_micros() as u64 +} + +#[derive(Default)] +pub struct Timestep { + current_time_us: u64, + accumulator: f32, + time_micros: u64, + ticks: u32, + speed: f32, + pub alpha: f32, + delta: f32, + loopnum: u8, +} + +impl Timestep { + pub fn new() -> Timestep { + let mut timestep = Timestep { + speed: 1.0, + ..Default::default() + }; + + timestep.reset(); + timestep + } + + fn calculate_alpha(&mut self) { + self.alpha = (self.accumulator / self.delta).clamp(0.0, 1.0); + } + + pub fn set_tps(&mut self, tps: f32) { + self.delta = 1000.0 / tps; + } + + pub fn reset(&mut self) { + self.current_time_us = get_micros(); + self.accumulator = 0.0; + } + + pub fn on_tick(&mut self) -> bool { + let newtime = get_micros(); + let frametime = newtime - self.current_time_us; + self.time_micros += frametime; + self.current_time_us = newtime; + self.accumulator += frametime as f32 * self.speed / 1000.0; + self.calculate_alpha(); + + if self.accumulator >= self.delta { + self.accumulator -= self.delta; + self.loopnum += 1; + self.ticks += 1; + + if self.loopnum > 5 { + // cannot keep up! + self.loopnum = 0; + self.accumulator = 0.0; + return false; + } + + true + } else { + self.loopnum = 0; + false + } + } +} diff --git a/uidev/src/vulkan.rs b/uidev/src/vulkan.rs new file mode 100644 index 0000000..17e22a7 --- /dev/null +++ b/uidev/src/vulkan.rs @@ -0,0 +1,216 @@ +use std::sync::{Arc, OnceLock}; +use vulkano::{ + device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + instance::{Instance, InstanceCreateInfo}, +}; +use wgui::gfx::WGfx; + +static VULKAN_LIBRARY: OnceLock> = OnceLock::new(); +fn get_vulkan_library() -> &'static Arc { + VULKAN_LIBRARY.get_or_init(|| vulkano::VulkanLibrary::new().unwrap()) // want panic +} + +#[allow(clippy::type_complexity)] +pub fn init_window( + title: &str, +) -> anyhow::Result<( + Arc, + winit::event_loop::EventLoop<()>, + Arc, + Arc, +)> { + use vulkano::{instance::InstanceCreateFlags, swapchain::Surface}; + use winit::{event_loop::EventLoop, window::Window}; + + let event_loop = EventLoop::new().unwrap(); // want panic + let mut vk_instance_extensions = Surface::required_extensions(&event_loop).unwrap(); + vk_instance_extensions.khr_get_physical_device_properties2 = true; + log::debug!("Instance exts for runtime: {:?}", &vk_instance_extensions); + + let instance = Instance::new( + get_vulkan_library().clone(), + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: vk_instance_extensions, + ..Default::default() + }, + )?; + + #[allow(deprecated)] + let window = Arc::new( + event_loop + .create_window( + Window::default_attributes() + .with_transparent(true) + .with_title(title), + ) + .unwrap(), // want panic + ); + let surface = Surface::from_window(instance.clone(), window.clone())?; + + let mut device_extensions = DeviceExtensions::empty(); + device_extensions.khr_swapchain = true; + + log::debug!("Device exts for app: {:?}", &device_extensions); + + let (physical_device, mut my_extensions, queue_families) = instance + .enumerate_physical_devices()? + .filter_map(|p| { + if p.supported_extensions().contains(&device_extensions) { + Some((p, device_extensions)) + } else { + log::debug!( + "Not using {} because it does not implement the following device extensions:", + p.properties().device_name, + ); + for (ext, missing) in p.supported_extensions().difference(&device_extensions) { + if missing { + log::debug!(" {ext}"); + } + } + None + } + }) + .filter_map(|(p, my_extensions)| { + try_all_queue_families(p.as_ref()).map(|families| (p, my_extensions, families)) + }) + .min_by_key(|(p, _, _)| prio_from_device_type(p)) + .expect("no suitable physical device found"); + + log::info!( + "Using vkPhysicalDevice: {}", + physical_device.properties().device_name, + ); + + if physical_device.supported_extensions().img_filter_cubic { + my_extensions.img_filter_cubic = true; + log::info!("img_filter_cubic!"); + } + + let (device, queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: my_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + queue_create_infos: queue_families + .iter() + .map(|fam| QueueCreateInfo { + queue_family_index: fam.queue_family_index, + queues: fam.priorities.clone(), + ..Default::default() + }) + .collect::>(), + ..Default::default() + }, + )?; + + let (queue_gfx, queue_xfer, _) = unwrap_queues(queues.collect()); + + let me = WGfx::new_from_raw( + instance, + device, + queue_gfx, + queue_xfer, + vulkano::format::Format::B8G8R8A8_UNORM, + ); + Ok((me, event_loop, window, surface)) +} + +#[derive(Debug)] +struct QueueFamilyLayout { + queue_family_index: u32, + priorities: Vec, +} + +fn prio_from_device_type(physical_device: &PhysicalDevice) -> u32 { + match physical_device.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + _ => 4, + } +} + +fn unwrap_queues(queues: Vec>) -> (Arc, Arc, Option>) { + match queues[..] { + [ref g, ref t, ref c] => (g.clone(), t.clone(), Some(c.clone())), + [ref gt, ref c] => (gt.clone(), gt.clone(), Some(c.clone())), + [ref gt] => (gt.clone(), gt.clone(), None), + _ => unreachable!(), + } +} + +fn try_all_queue_families(physical_device: &PhysicalDevice) -> Option> { + queue_families_priorities( + physical_device, + vec![ + // main-thread graphics + uploads + QueueFlags::GRAPHICS | QueueFlags::TRANSFER, + // capture-thread uploads + QueueFlags::TRANSFER, + ], + ) + .or_else(|| { + queue_families_priorities( + physical_device, + vec![ + // main thread graphics + QueueFlags::GRAPHICS, + // main thread uploads + QueueFlags::TRANSFER, + // capture thread uploads + QueueFlags::TRANSFER, + ], + ) + }) + .or_else(|| { + queue_families_priorities( + physical_device, + // main thread-only. software capture not supported. + vec![QueueFlags::GRAPHICS | QueueFlags::TRANSFER], + ) + }) +} + +fn queue_families_priorities( + physical_device: &PhysicalDevice, + mut requested_queues: Vec, +) -> Option> { + let mut result = Vec::with_capacity(3); + + for (idx, props) in physical_device.queue_family_properties().iter().enumerate() { + let mut remaining = props.queue_count; + let mut want = 0usize; + + requested_queues.retain(|requested| { + if props.queue_flags.intersects(*requested) && remaining > 0 { + remaining -= 1; + want += 1; + false + } else { + true + } + }); + + if want > 0 { + result.push(QueueFamilyLayout { + queue_family_index: idx as u32, + priorities: std::iter::repeat_n(1.0, want).collect(), + }); + } + } + + if requested_queues.is_empty() { + log::debug!("Selected GPU queue families: {result:?}"); + Some(result) + } else { + None + } +} diff --git a/wgui/.editorconfig b/wgui/.editorconfig new file mode 100644 index 0000000..f5834b7 --- /dev/null +++ b/wgui/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*.rs] +indent_style = tab +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/wgui/.gitignore b/wgui/.gitignore new file mode 100644 index 0000000..7be846b --- /dev/null +++ b/wgui/.gitignore @@ -0,0 +1,2 @@ +target +.vscode \ No newline at end of file diff --git a/wgui/Cargo.lock b/wgui/Cargo.lock new file mode 100644 index 0000000..c8d3a40 --- /dev/null +++ b/wgui/Cargo.lock @@ -0,0 +1,1966 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cc" +version = "1.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "cosmic-text" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" +dependencies = [ + "bitflags 2.9.1", + "fontdb", + "log", + "rangemap", + "rustc-hash 1.1.0", + "rustybuzz", + "self_cell", + "smol_str", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.1", + "objc2", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "font-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "glam" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" + +[[package]] +name = "grid" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969d090eea77fac1be33f853ffc4b6b60cc6f312ddf4fd28b311a730e1dd8ebe" + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "gif", + "image-webp", + "num-traits", + "png", + "ravif", + "rayon", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "kurbo" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" +dependencies = [ + "cfg-if", + "windows-targets 0.53.0", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[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 = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.1", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874" +dependencies = [ + "bitflags 2.9.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.1", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce8e2ca6b24313587a03ca61bb74c384e2a815bd90cf2866cfc9f5fb7a11fa0" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "self_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "shaderc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e07913ada18607bb60d12431cbe3358d3bbebbe95948e1618851dc01e63b7b" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7" +dependencies = [ + "cmake", + "libc", + "roxmltree 0.14.1", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "skrifa" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe6666ab11018ab91ff7b03f1a3b9fdbecfb610848436fefa5ce50343d3d913" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slabbin" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db491c0d4152a069911a0fbdaca959691bf0b9d7110d98a7ed1c8e59b79ab30" + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dce3f0af95643c855cdc449fbaa17d8c2cd08e0b00a49a6babcbe6e71667f3d" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "taffy" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592835b1f82a500b55aa6242e5e2d1fb90c50e701fa9c33b5962d4aeaa4e88d6" +dependencies = [ + "arrayvec", + "grid", + "serde", + "slotmap", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64", + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "xmlwriter", +] + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vk-parse" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3859da4d7b98bec73e68fb65815d47a263819c415c90eed42b80440a02cbce8c" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "vulkano" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08840c2b51759a6f88f26f5ea378bc8b5c199a5b4760ddda292304be087249c4" +dependencies = [ + "ash", + "bytemuck", + "crossbeam-queue", + "foldhash", + "half", + "heck 0.4.1", + "indexmap", + "libloading", + "nom", + "once_cell", + "parking_lot", + "proc-macro2", + "quote", + "raw-window-handle", + "raw-window-metal", + "serde", + "serde_json", + "slabbin", + "smallvec", + "thread_local", + "vk-parse", + "vulkano-macros", +] + +[[package]] +name = "vulkano-macros" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc929c42c9336fd082079ac3ea30126e4a0dfe36fd2e2b3581303f7d140d20f" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "vulkano-shaders" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf501461be7cef2893c0e62c50945add9763cc482051d29053f6157089d5ea9" +dependencies = [ + "foldhash", + "heck 0.4.1", + "proc-macro2", + "quote", + "shaderc", + "syn", + "vulkano", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + +[[package]] +name = "wgui" +version = "0.1.0" +dependencies = [ + "anyhow", + "cosmic-text", + "etagere", + "glam", + "image", + "log", + "lru", + "resvg", + "roxmltree 0.20.0", + "rustc-hash 2.1.1", + "slotmap", + "smallvec", + "taffy", + "vulkano", + "vulkano-shaders", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "xml-rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/wgui/Cargo.toml b/wgui/Cargo.toml new file mode 100644 index 0000000..d5ff3c6 --- /dev/null +++ b/wgui/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "wgui" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = { workspace = true } +cosmic-text = "0.15.0" +etagere = "0.2.15" +glam = { workspace = true } +image = { version = "0.25.9", default-features = false, features = [ + "gif", + "jpeg", + "png", + "rayon", + "webp", +] } +log = { workspace = true } +lru = "0.16.2" +ouroboros = "0.18.5" +parking_lot = "0.12.5" +regex = { workspace = true } +resvg = { version = "0.45.1", default-features = false } +roxmltree = "0.21.1" +rustc-hash = "2.1.1" +serde_json = "1.0.145" +slotmap = { workspace = true } +smallvec = "1.15.1" +taffy = "0.9.2" +vulkano = { workspace = true } +vulkano-shaders = { workspace = true } +rust-embed = { workspace = true } +flate2 = "1.1.5" diff --git a/wgui/README.md b/wgui/README.md new file mode 100644 index 0000000..7c63823 --- /dev/null +++ b/wgui/README.md @@ -0,0 +1,9 @@ +

+  logo +

+ +# wgui + +an experimental gui library for wlx-overlay-s and WayVR Dashboard + +powered via vulkan diff --git a/wgui/assets/wgui/close.svg b/wgui/assets/wgui/close.svg new file mode 100644 index 0000000..1aeb398 --- /dev/null +++ b/wgui/assets/wgui/close.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/wgui/assets/wgui/window_frame.xml b/wgui/assets/wgui/window_frame.xml new file mode 100644 index 0000000..8fdb7cf --- /dev/null +++ b/wgui/assets/wgui/window_frame.xml @@ -0,0 +1,31 @@ + + + + + +
+
+
+ +
+ + +
+ +
+
+
+
\ No newline at end of file diff --git a/wgui/contrib/logo.png b/wgui/contrib/logo.png new file mode 100644 index 0000000..70f25bb Binary files /dev/null and b/wgui/contrib/logo.png differ diff --git a/wgui/doc/widgets.md b/wgui/doc/widgets.md new file mode 100644 index 0000000..0587c25 --- /dev/null +++ b/wgui/doc/widgets.md @@ -0,0 +1,381 @@ +# Quick jump + +## [Widgets](#widgets) + +[div](#div-widget), [label](#label-widget), [rectangle](#rectangle-widget), [sprite](#sprite-widget) + +## [Built-in components](#components) + +[Button](#button-component), [Slider](#slider-component), [CheckBox](#checkbox-component) + +## [Examples](#examples) + +[Simple layout](#simple-layout) + +[Value substitution (themes)](#value-substitution-themes) + +[File inclusion](#file-inclusion) + +[Macros](#macros) + +[Templates](#templates) + +# Universal widget attributes + +_They can be used in any widget/component._ + +`display`: "flex" | "block" | "grid" + +`position`: "absolute" | "relative" + +`flex_grow`: **float** + +`flex_shrink`: **float** + +`gap`: **float** | **percent** + +`flex_basis`: **float** | **percent** + +`justify_self`: "center" | "end" | "flex_end" | "flex_start" | "start" | "stretch" + +`justify_content`: "center" | "end" | "flex_start" | "flex_end" | "space_around" | "space_between" | "space_evenly" | "start" | "stretch" + +`flex_wrap`: "wrap" | "no_wrap" | "wrap_reverse" + +`flex_direction`: "row" | "column" | "column_reverse" | "row_reverse" + +`align_items`, `align_self`: "baseline" | "center" | "end" | "flex_start" | "flex_end" | "start" | "stretch" + +`box_sizing`: "border_box" | "content_box" + +`margin`, `margin_left`, `margin_right`, `margin_top`, `margin_bottom`: **float** | **percent** + +`padding`, `padding_left`, `padding_right`, `padding_top`, `padding_bottom`: **float** | **percent** + +`overflow`, `overflow_x`, `overflow_y`: "hidden" | "visible" | "clip" | "scroll" + +`min_width`, `min_height`: **float** | **percent** + +`max_width`, `max_height`: **float** | **percent** + +`width`, `height`: **float** | **percent** + +### Advanced attributes + +`interactable`: "1" | "0" + +_Set to 0 if you want to exclude this widget from altering the event state_ + +`new_pass`: "1" | "0" + +_Set to 1 if you want to render overlapping pop-ups to properly render your widgets in order. Wgui renders with as few Vulkan drawcalls as possible, so this is your responsibility._ + +# Widgets + +## div widget + +### `
` + +### The most simple element + +#### Parameters + +_None_ + +--- + +## label widget + +### `