From f193f33f4e4608e6d02d7b58523cf3f70e1a872a Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Thu, 9 Nov 2023 22:27:05 +0900 Subject: [PATCH] nothing works --- .gitignore | 2 + Cargo.lock | 4155 +++++++++++++++++++++++++ Cargo.toml | 38 + src/backend/common.rs | 152 + src/backend/mod.rs | 3 + src/backend/openvr/input.rs | 288 ++ src/backend/openvr/mod.rs | 100 + src/backend/openvr/overlay.rs | 15 + src/backend/openxr.rs | 0 src/graphics.rs | 559 ++++ src/graphics.rs.bak2 | 1197 +++++++ src/gui/font.rs | 211 ++ src/gui/mod.rs | 669 ++++ src/image.png | Bin 0 -> 148030 bytes src/input.rs | 431 +++ src/main.rs | 271 ++ src/main.rs.bak2 | 159 + src/overlays/interactions.rs | 41 + src/overlays/keyboard.rs | 354 +++ src/overlays/mod.rs | 145 + src/overlays/screen.rs | 31 + src/overlays/watch.rs | 170 + src/ovr.rs | 64 + src/res/421581.wav | Bin 0 -> 11214 bytes src/res/660533.wav | Bin 0 -> 125612 bytes src/res/actions.json | 83 + src/res/actions_binding_knuckles.json | 173 + src/res/actions_binding_oculus.json | 139 + src/res/actions_binding_vive.json | 126 + src/res/keyboard.yaml | 127 + src/shaders/frag-color.spv | Bin 0 -> 696 bytes src/shaders/frag-glyph.spv | Bin 0 -> 1148 bytes src/shaders/frag-sprite.spv | Bin 0 -> 764 bytes src/shaders/frag-srgb.spv | Bin 0 -> 1316 bytes src/shaders/mod.rs | 108 + src/shaders/src/color.frag | 15 + src/shaders/src/common.vert | 12 + src/shaders/src/glyph.frag | 18 + src/shaders/src/sprite.frag | 14 + src/shaders/src/srgb.frag | 19 + src/shaders/vert-common.spv | Bin 0 -> 964 bytes src/state.rs | 103 + 42 files changed, 9992 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/backend/common.rs create mode 100644 src/backend/mod.rs create mode 100644 src/backend/openvr/input.rs create mode 100644 src/backend/openvr/mod.rs create mode 100644 src/backend/openvr/overlay.rs create mode 100644 src/backend/openxr.rs create mode 100644 src/graphics.rs create mode 100644 src/graphics.rs.bak2 create mode 100644 src/gui/font.rs create mode 100644 src/gui/mod.rs create mode 100644 src/image.png create mode 100644 src/input.rs create mode 100644 src/main.rs create mode 100644 src/main.rs.bak2 create mode 100644 src/overlays/interactions.rs create mode 100644 src/overlays/keyboard.rs create mode 100644 src/overlays/mod.rs create mode 100644 src/overlays/screen.rs create mode 100644 src/overlays/watch.rs create mode 100644 src/ovr.rs create mode 100644 src/res/421581.wav create mode 100644 src/res/660533.wav create mode 100644 src/res/actions.json create mode 100644 src/res/actions_binding_knuckles.json create mode 100644 src/res/actions_binding_oculus.json create mode 100644 src/res/actions_binding_vive.json create mode 100644 src/res/keyboard.yaml create mode 100644 src/shaders/frag-color.spv create mode 100644 src/shaders/frag-glyph.spv create mode 100644 src/shaders/frag-sprite.spv create mode 100644 src/shaders/frag-srgb.spv create mode 100644 src/shaders/mod.rs create mode 100644 src/shaders/src/color.frag create mode 100644 src/shaders/src/common.vert create mode 100644 src/shaders/src/glyph.frag create mode 100644 src/shaders/src/sprite.frag create mode 100644 src/shaders/src/srgb.frag create mode 100644 src/shaders/vert-common.spv create mode 100644 src/state.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..265b7f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.gdb_history diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..dac4990 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4155 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" +dependencies = [ + "alsa-sys", + "bitflags 1.3.2", + "libc", + "nix 0.24.3", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "android-activity" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" +dependencies = [ + "android-properties", + "bitflags 1.3.2", + "cc", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum 0.6.1", +] + +[[package]] +name = "android-properties" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f" +dependencies = [ + "itertools 0.9.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "ash-window" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b912285a7c29f3a8f87ca6f55afc48768624e5e33ec17dbd2f2075903f5e35ab" +dependencies = [ + "ash", + "raw-window-handle", + "raw-window-metal", +] + +[[package]] +name = "ashpd" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c018490e423efb6f032ef575f873ea57b61d44bec763cfe027b8e8852a027cf" +dependencies = [ + "async-std", + "enumflags2", + "futures-channel", + "futures-util", + "once_cell", + "rand", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" +dependencies = [ + "async-lock 2.8.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite 1.13.0", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "blocking", + "futures-lite 1.13.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997" +dependencies = [ + "async-lock 3.0.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.0.1", + "parking", + "polling 3.3.0", + "rustix 0.38.21", + "slab", + "tracing", + "waker-fn", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e900cdcd39bb94a14487d3f7ef92ca222162e6c7c3fe7cb3550ea75fb486ed" +dependencies = [ + "event-listener 3.0.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.0.1", + "futures-lite 1.13.0", + "rustix 0.38.21", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.2.0", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.21", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "autocxx" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba64dd33efd8f09724143d45ab91b48aebcee52f4fb11add3464c998fab47dc" +dependencies = [ + "aquamarine", + "autocxx-macro", + "cxx", + "moveit", +] + +[[package]] +name = "autocxx-bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c9fb7b8dd83a582e12157367773d8d1195f2dea54d4250aaf3426abae3237aa" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "itertools 0.10.5", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.39", + "which", +] + +[[package]] +name = "autocxx-build" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955e602d2d68b79ca5d674984259234fad2c8d869ad99011699e0a3cd76f38cd" +dependencies = [ + "autocxx-engine", + "env_logger 0.9.3", + "indexmap 1.9.3", + "syn 2.0.39", +] + +[[package]] +name = "autocxx-engine" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5918896fc1d44a647345fd5e8c74208424e394a76bdd2942398f4aff81ec7ab1" +dependencies = [ + "aquamarine", + "autocxx-bindgen", + "autocxx-parser", + "cc", + "cxx-gen", + "indexmap 1.9.3", + "indoc", + "itertools 0.10.5", + "log", + "miette", + "once_cell", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustversion", + "serde_json", + "strum_macros 0.24.3", + "syn 2.0.39", + "tempfile", + "thiserror", + "version_check", +] + +[[package]] +name = "autocxx-macro" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e594e68d030b6eb1ce7e2b40958f4f4ae7150c588c76d76b9f8178d41c47d80" +dependencies = [ + "autocxx-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "autocxx-parser" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef00b2fc378804c31c4fbd693a7fea93f8a90467dce331dae1e4ce41e542953" +dependencies = [ + "indexmap 1.9.3", + "itertools 0.10.5", + "log", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.39", + "thiserror", +] + +[[package]] +name = "bindgen" +version = "0.66.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.39", +] + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.39", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + +[[package]] +name = "blocking" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" +dependencies = [ + "async-channel", + "async-lock 2.8.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 1.13.0", + "piper", + "tracing", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "calloop" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +dependencies = [ + "bitflags 1.3.2", + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +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.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading 0.7.4", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics 0.23.1", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-cstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie-factory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "coreaudio-rs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +dependencies = [ + "bitflags 1.3.2", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8478e5bdad14dce236b9898ea002eabfa87cbe14f0aa538dbe3b6a4bec4332d" +dependencies = [ + "bindgen 0.68.1", +] + +[[package]] +name = "cpal" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "dasp_sample", + "jni 0.19.0", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "oboe", + "once_cell", + "parking_lot", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[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 = "cstr" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aa998c33a6d3271e3678950a22134cd7dd27cef86dee1b611b5b14207d1d90b" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "cxx" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7129e341034ecb940c9072817cd9007974ea696844fc4dd582dc1653a7fbe2e8" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-gen" +version = "0.7.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e0fc77e9f8d61724be90deb42a7e50ba3bf37c7c16dc91cdba821f69a5e0e9" +dependencies = [ + "codespan-reporting", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06fdd177fc61050d63f67f5bd6351fac6ab5526694ea8e359cd9cd3b75857f44" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "587663dd5fb3d10932c8aecfe7c844db1bcf0aee93eeab08fac13dc1212c2e7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[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 = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.1", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cec0252c2afff729ee6f00e903d479fba81784c8e2bd77447673471fdfaea1" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +dependencies = [ + "event-listener 3.0.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fdeflate" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fontconfig-rs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4baadad5111c6820e97fc8bde5077258e6f272b5b38538db4b42e1812f29f3" +dependencies = [ + "const-cstr", + "once_cell", + "thiserror", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freetype-rs" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59c337e64822dd56a3a83ed75a662a470736bdb3a9fabfb588dff276b94a4e0" +dependencies = [ + "bitflags 1.3.2", + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643148ca6cbad6bec384b52fbe1968547d578c4efe83109e035c43a71734ff88" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" +dependencies = [ + "approx", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "half" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idmap" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba885f996064df334b1639785897d1c58c0646750101b839b8359664cb26c0e" +dependencies = [ + "fixedbitset", +] + +[[package]] +name = "idmap-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f44aa1b60e7de7e2833ad2cc3bc79880171d1e1efdb44ca833a1acf8102870" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", +] + +[[package]] +name = "indoc" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" + +[[package]] +name = "input-linux" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f403224ea808abe6182696e3a36d9875c0e942ba7c99239f9ef545b96e35606" +dependencies = [ + "input-linux-sys", + "nix 0.26.4", +] + +[[package]] +name = "input-linux-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a687a25a4973027df9153753a5589f97fe1e958f694a34eea5606ae65299ab8" +dependencies = [ + "libc", + "nix 0.26.4", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.3", + "rustix 0.38.21", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libspa" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434617020ddca18b86067912970c55410ca654cdafd775480322f50b857a8c4" +dependencies = [ + "bitflags 2.4.1", + "cc", + "convert_case 0.6.0", + "cookie-factory", + "libc", + "libspa-sys", + "nix 0.26.4", + "nom", + "system-deps", +] + +[[package]] +name = "libspa-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e70ca3f3e70f858ef363046d06178c427b4e0b63d210c95fd87d752679d345" +dependencies = [ + "bindgen 0.66.1", + "cc", + "system-deps", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "moveit" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d7335204cb6ef7bd647fa6db0be3e4d7aa25b5823a7aa030027ddf512cefba" +dependencies = [ + "cxx", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum 0.5.11", + "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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", +] + +[[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 = "normpath" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04aaf5e9cb0fbf883cc0423159eacdf96a9878022084b35c462c428cab73bcaf" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "oboe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0" +dependencies = [ + "jni 0.20.0", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2" +dependencies = [ + "cc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "ovr_overlay" +version = "0.0.0" +dependencies = [ + "byteorder", + "derive_more", + "enumset", + "lazy_static", + "log", + "ovr_overlay_sys", + "slice-of-array", + "thiserror", +] + +[[package]] +name = "ovr_overlay_sys" +version = "0.0.0" +dependencies = [ + "autocxx", + "autocxx-build", + "cxx", + "normpath", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pipewire" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d009c8dd65e890b515a71950f7e4c801523b8894ff33863a40830bf762e9e9" +dependencies = [ + "anyhow", + "bitflags 2.4.1", + "libc", + "libspa", + "libspa-sys", + "nix 0.26.4", + "once_cell", + "pipewire-sys", + "thiserror", +] + +[[package]] +name = "pipewire-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "890c084e7b737246cb4799c86b71a0e4da536031ff7473dd639eba9f95039f64" +dependencies = [ + "bindgen 0.66.1", + "libspa-sys", + "system-deps", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.21", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.39", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac4ea493258d54c24cb46aa9345d099e58e2ea3f30dd63667fc54fc892f18e76" +dependencies = [ + "cocoa", + "core-graphics 0.23.1", + "objc", + "raw-window-handle", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rodio" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1bb7b48ee48471f55da122c0044fcc7600cfcc85db88240b89cb832935e611" +dependencies = [ + "cpal", + "hound", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.11", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rxscreen" +version = "0.1.6" +source = "git+https://github.com/galister/rxscreen.git#1d0eb4f66c0cd5f1ddfc7ee2bab930019cb9e0d5" +dependencies = [ + "libc", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[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.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.5.10", + "smithay-client-toolkit 0.16.1", + "tiny-skia", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +dependencies = [ + "indexmap 2.1.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shaderc" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31cef52787a0db5108788ea20bed13d6bf4b96287c5c5201e55725f7070f3443" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8f8439fffcffd6efcd74197204addf935dbab5752696bd990a6cd36d54cf64" +dependencies = [ + "cmake", + "libc", + "roxmltree", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-of-array" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f120bb98cb4cb0dab21c882968c3cbff79dd23b46f07b1cf5c25044945ce84" + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "smithay-client-toolkit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2 0.5.10", + "nix 0.24.3", + "pkg-config", + "wayland-client 0.29.5", + "wayland-cursor 0.29.5", + "wayland-protocols 0.29.5", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1476c3d89bb67079264b88aaf4f14358353318397e083b7c4e8c14517f55de7" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2 0.5.10", + "nix 0.26.4", + "pkg-config", + "thiserror", + "wayland-backend", + "wayland-client 0.30.2", + "wayland-cursor 0.30.0", + "wayland-protocols 0.30.1", + "wayland-protocols-wlr", + "wayland-scanner 0.30.1", + "xkbcommon", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.39", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.21", + "windows-sys 0.48.0", +] + +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-skia" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.21.0", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +dependencies = [ + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "value-bag" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vk-parse" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6a0bda9bbe6b9e50e6456c80aa8fe4cca3b21e4311a1130c41e4915ec2e32a" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "vulkano" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1f15eeb9d93a05eb3c237332a10806eac1eb82444e54485bfcc1859c483c23" +dependencies = [ + "ahash", + "ash", + "bytemuck", + "core-graphics-types", + "crossbeam-queue", + "half", + "heck", + "indexmap 1.9.3", + "libloading 0.7.4", + "objc", + "once_cell", + "parking_lot", + "proc-macro2", + "quote", + "regex", + "serde", + "serde_json", + "smallvec", + "thread_local", + "vk-parse", + "vulkano-macros", +] + +[[package]] +name = "vulkano-macros" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "895b8a2cac1e7650d2d0552f2392da0970a358515ac11a34adaf19bfdc771b98" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "vulkano-shaders" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f8cf18e9becbc6d39f1c39e26bcf69546c93989553eb5748cd734a8a697a6e5" +dependencies = [ + "ahash", + "heck", + "proc-macro2", + "quote", + "shaderc", + "syn 1.0.109", + "vulkano", +] + +[[package]] +name = "vulkano-util" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a71b6df05a391161c1baec645a918437c2949d3494bf74c8358fde291d37f5f4" +dependencies = [ + "ahash", + "vulkano", + "vulkano-win", + "winit", +] + +[[package]] +name = "vulkano-win" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666c77efe5ea82837781961a6bcd957ee2e926777e8de0005f580335d6eaefe7" +dependencies = [ + "core-graphics-types", + "objc", + "raw-window-handle", + "vulkano", + "winit", +] + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + +[[package]] +name = "wayland-backend" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b48e27457e8da3b2260ac60d0a94512f5cba36448679f3747c0865b7893ed8" +dependencies = [ + "cc", + "downcast-rs", + "io-lifetimes", + "nix 0.26.4", + "scoped-tls", + "smallvec", + "wayland-sys 0.30.1", +] + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner 0.29.5", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-client" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "nix 0.26.4", + "wayland-backend", + "wayland-scanner 0.30.1", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client 0.29.5", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0c3a0d5b4b688b07b0442362d3ed6bf04724fcc16cd69ab6285b90dbc487aa" +dependencies = [ + "nix 0.26.4", + "wayland-client 0.30.2", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client 0.29.5", + "wayland-commons", + "wayland-scanner 0.29.5", +] + +[[package]] +name = "wayland-protocols" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b28101e5ca94f70461a6c2d610f76d85ad223d042dd76585ab23d3422dd9b4d" +dependencies = [ + "bitflags 1.3.2", + "wayland-backend", + "wayland-client 0.30.2", + "wayland-scanner 0.30.1", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a" +dependencies = [ + "bitflags 1.3.2", + "wayland-backend", + "wayland-client 0.30.2", + "wayland-protocols 0.30.1", + "wayland-scanner 0.30.1", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-scanner" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b873b257fbc32ec909c0eb80dea312076a67014e65e245f5eb69a6b8ab330e" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "wayland-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.21", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[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_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_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_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_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_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_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_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 = "winit" +version = "0.28.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +dependencies = [ + "android-activity", + "bitflags 1.3.2", + "cfg_aliases", + "core-foundation", + "core-graphics 0.22.3", + "dispatch", + "instant", + "libc", + "log", + "mio", + "ndk", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle", + "redox_syscall 0.3.5", + "sctk-adwaita", + "smithay-client-toolkit 0.16.1", + "wasm-bindgen", + "wayland-client 0.29.5", + "wayland-commons", + "wayland-protocols 0.29.5", + "wayland-scanner 0.29.5", + "web-sys", + "windows-sys 0.45.0", + "x11-dl", +] + +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +dependencies = [ + "memchr", +] + +[[package]] +name = "wlx-capture" +version = "0.1.0" +dependencies = [ + "ashpd", + "drm-fourcc", + "libc", + "libspa-sys", + "log", + "once_cell", + "pipewire", + "rxscreen", + "smithay-client-toolkit 0.17.0", + "wayland-client 0.30.2", + "wayland-protocols 0.30.1", + "xcb", +] + +[[package]] +name = "wlx-overlay-s" +version = "0.1.0" +dependencies = [ + "ash-window", + "chrono", + "cstr", + "env_logger 0.10.0", + "fontconfig-rs", + "freetype-rs", + "glam", + "idmap", + "idmap-derive", + "input-linux", + "libc", + "log", + "once_cell", + "ovr_overlay", + "png", + "raw-window-handle", + "regex", + "rodio", + "serde", + "serde_yaml", + "smallvec", + "strum", + "tinyvec", + "vulkano", + "vulkano-shaders", + "vulkano-util", + "vulkano-win", + "winit", + "wlx-capture", +] + +[[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 = "xcb" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3acf6b0945550d37d3a683b8f7de9d9f66b2c14dc390313b34d7ac6f1b4089" +dependencies = [ + "bitflags 1.3.2", + "libc", + "quick-xml", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xdg-home" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +dependencies = [ + "nix 0.26.4", + "winapi", +] + +[[package]] +name = "xkbcommon" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52db25b599e92bf6e3904134618728eeb7b49a5a4f38f107f92399bb9c496b88" +dependencies = [ + "libc", + "memmap2 0.7.1", +] + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" +dependencies = [ + "const-cstr", + "dlib", + "once_cell", + "pkg-config", +] + +[[package]] +name = "zbus" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "zvariant" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bf8c76e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "wlx-overlay-s" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ash-window = "0.12.0" +chrono = "0.4.29" +cstr = "0.2.11" +env_logger = "0.10.0" +fontconfig-rs = { version = "0.1.1", features = ["dlopen"] } +freetype-rs = "0.32.0" +glam = { version = "0.24.1", features = ["approx"] } +idmap = "0.2.21" +idmap-derive = "0.1.2" +input-linux = "0.6.0" +libc = "0.2.147" +log = "0.4.20" +once_cell = "1.18.0" +ovr_overlay = { features = ["ovr_input", "ovr_system"], path = "../ovr_overlay_oyasumi" } +png = "0.17.10" +raw-window-handle = "0.5.2" +regex = "1.9.5" +rodio = { version = "0.17.1", default-features = false, features = ["wav", "hound"] } +serde = { version = "1.0.188", features = ["derive"] } +serde_yaml = "0.9.25" +smallvec = "1.11.0" +strum = { version = "0.25.0", features = ["derive"] } +tinyvec = "1.6.0" +vulkano = { version = "0.33.0", features = ["serde"] } +vulkano-shaders = "0.33.0" +vulkano-util = "0.33.0" +vulkano-win = "0.33.0" +winit = "0.28.6" +wlx-capture = { version = "0.1.0", path = "../wlx-capture" } + diff --git a/src/backend/common.rs b/src/backend/common.rs new file mode 100644 index 0000000..25bff55 --- /dev/null +++ b/src/backend/common.rs @@ -0,0 +1,152 @@ +use std::{collections::VecDeque, time::Instant}; + +use glam::{Affine3A, Vec2, Vec3, Vec3A}; +use ovr_overlay::TrackedDeviceIndex; + +pub struct InputState { + pub hmd: Affine3A, + pub pointers: [Pointer; 2], + pub devices: Vec, + pub(super) data: TState, +} + +impl InputState { + pub 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) { + for hand in &mut self.pointers { + 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)) * (1.0 - 2.0 * hand.hand as f32); + + 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; + } + } + _ => {} + }; + } + } +} + +pub struct Pointer { + pub hand: usize, + pub pose: Affine3A, + pub now: PointerState, + pub before: PointerState, + pub(super) interaction: InteractionState, + pub(super) data: THand, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct PointerState { + pub scroll: f32, + pub click: bool, + pub grab: bool, + pub alt_click: bool, + pub show_hide: bool, + pub space_drag: bool, + pub click_modifier_right: bool, + pub click_modifier_middle: bool, +} + +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, +} + +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(), + } + } +} + +pub struct PointerHit { + pub hand: usize, + pub mode: PointerMode, + pub primary: bool, + pub uv: Vec2, + pub dist: f32, +} + +struct RayHit { + idx: usize, + ray_pos: Vec3, + hit_pos: Vec3, + uv: Vec2, + dist: f32, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct GrabData { + pub offset: Vec3, + pub grabbed_id: usize, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, Default)] +pub enum PointerMode { + #[default] + Left, + Right, + Middle, +} + +pub struct TrackedDevice { + pub index: TrackedDeviceIndex, + pub valid: bool, + 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, +} diff --git a/src/backend/mod.rs b/src/backend/mod.rs new file mode 100644 index 0000000..55f52d2 --- /dev/null +++ b/src/backend/mod.rs @@ -0,0 +1,3 @@ +pub mod common; +pub mod openvr; +pub mod openxr; diff --git a/src/backend/openvr/input.rs b/src/backend/openvr/input.rs new file mode 100644 index 0000000..d943943 --- /dev/null +++ b/src/backend/openvr/input.rs @@ -0,0 +1,288 @@ +use std::array; + +use glam::Affine3A; +use ovr_overlay::{ + input::{ActionHandle, ActionSetHandle, ActiveActionSet, InputManager, InputValueHandle}, + sys::{ + k_unMaxTrackedDeviceCount, ETrackedControllerRole, ETrackedDeviceClass, + ETrackedDeviceProperty, ETrackingUniverseOrigin, HmdMatrix34_t, + }, + system::SystemManager, + TrackedDeviceIndex, +}; + +use crate::backend::common::{ + InputState, InteractionState, Pointer, PointerState, TrackedDevice, TrackedDeviceRole, +}; + +macro_rules! result_str { + ( $e:expr ) => { + match $e { + Ok(x) => Ok(x), + Err(y) => Err(y.description()), + } + }; +} + +const SET_DEFAULT: &str = "/actions/default"; +const INPUT_SOURCES: [&str; 2] = ["/user/hand/left", "/user/hand/right"]; +const PATH_POSES: [&str; 2] = [ + "/actions/default/in/LeftHand", + "/actions/default/in/RightHand", +]; +const PATH_HAPTICS: [&str; 2] = [ + "/actions/default/out/HapticsLeft", + "/actions/default/out/HapticsRight", +]; + +const PATH_CLICK: &str = "/actions/default/in/Click"; +const PATH_GRAB: &str = "/actions/default/in/Grab"; +const PATH_SCROLL: &str = "/actions/default/in/Scroll"; +const PATH_ALT_CLICK: &str = "/actions/default/in/AltClick"; +const PATH_SHOW_HIDE: &str = "/actions/default/in/ShowHide"; +const PATH_SPACE_DRAG: &str = "/actions/default/in/SpaceDrag"; +const PATH_CLICK_MODIFIER_RIGHT: &str = "/actions/default/in/ClickModifierRight"; +const PATH_CLICK_MODIFIER_MIDDLE: &str = "/actions/default/in/ClickModifierMiddle"; + +const INPUT_ANY: InputValueHandle = InputValueHandle(ovr_overlay::sys::k_ulInvalidInputValueHandle); + +pub(super) struct OpenVrInputState { + set_hnd: ActionSetHandle, + click_hnd: ActionHandle, + grab_hnd: ActionHandle, + scroll_hnd: ActionHandle, + alt_click_hnd: ActionHandle, + show_hide_hnd: ActionHandle, + space_drag_hnd: ActionHandle, + click_modifier_right_hnd: ActionHandle, + click_modifier_middle_hnd: ActionHandle, +} + +pub(super) struct OpenVrHandState { + has_pose: bool, + input_hnd: InputValueHandle, + pose_hnd: ActionHandle, + haptics_hnd: ActionHandle, +} + +impl InputState { + pub fn new(input: &mut InputManager) -> Result { + let set_hnd = result_str!(input.get_action_set_handle(SET_DEFAULT))?; + + let click_hnd = result_str!(input.get_action_handle(PATH_CLICK))?; + let grab_hnd = result_str!(input.get_action_handle(PATH_GRAB))?; + let scroll_hnd = result_str!(input.get_action_handle(PATH_SCROLL))?; + let alt_click_hnd = result_str!(input.get_action_handle(PATH_ALT_CLICK))?; + let show_hide_hnd = result_str!(input.get_action_handle(PATH_SHOW_HIDE))?; + let space_drag_hnd = result_str!(input.get_action_handle(PATH_SPACE_DRAG))?; + let click_modifier_right_hnd = + result_str!(input.get_action_handle(PATH_CLICK_MODIFIER_RIGHT))?; + let click_modifier_middle_hnd = + result_str!(input.get_action_handle(PATH_CLICK_MODIFIER_MIDDLE))?; + + let input_hnd: Vec = INPUT_SOURCES + .iter() + .map(|path| Ok(result_str!(input.get_input_source_handle(path))?)) + .collect::>()?; + + let pose_hnd: Vec = PATH_POSES + .iter() + .map(|path| Ok(result_str!(input.get_action_handle(path))?)) + .collect::>()?; + + let haptics_hnd: Vec = PATH_HAPTICS + .iter() + .map(|path| Ok(result_str!(input.get_action_handle(path))?)) + .collect::>()?; + + let hands: [Pointer; 2] = array::from_fn(|i| Pointer:: { + hand: i, + now: PointerState::default(), + before: PointerState::default(), + pose: Affine3A::IDENTITY, + interaction: InteractionState::default(), + data: OpenVrHandState { + has_pose: false, + input_hnd: input_hnd[i], + pose_hnd: pose_hnd[i], + haptics_hnd: haptics_hnd[i], + }, + }); + + Ok(InputState { + hmd: Affine3A::IDENTITY, + pointers: hands, + devices: vec![], + data: OpenVrInputState { + set_hnd, + click_hnd, + grab_hnd, + scroll_hnd, + alt_click_hnd, + show_hide_hnd, + space_drag_hnd, + click_modifier_right_hnd, + click_modifier_middle_hnd, + }, + }) + } + + pub fn update(&mut self, input: &mut InputManager, system: &mut SystemManager) { + let aas = ActiveActionSet { + 0: ovr_overlay::sys::VRActiveActionSet_t { + ulActionSet: self.data.set_hnd.0, + ulRestrictedToDevice: 0, + ulSecondaryActionSet: 0, + unPadding: 0, + nPriority: 0, + }, + }; + + let _ = input.update_actions(&mut [aas]); + + let universe = ETrackingUniverseOrigin::TrackingUniverseStanding; + + for i in 0..2 { + let hand = &mut self.pointers[i]; + + hand.data.has_pose = false; + + let _ = input + .get_pose_action_data_relative_to_now( + hand.data.pose_hnd, + universe.clone(), + 0.005, + INPUT_ANY, + ) + .and_then(|pose| { + copy_from_hmd(&pose.0.pose.mDeviceToAbsoluteTracking, &mut hand.pose); + hand.data.has_pose = true; + Ok(()) + }); + + hand.now.click = input + .get_digital_action_data(self.data.click_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.grab = input + .get_digital_action_data(self.data.grab_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.alt_click = input + .get_digital_action_data(self.data.alt_click_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.show_hide = input + .get_digital_action_data(self.data.show_hide_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.space_drag = input + .get_digital_action_data(self.data.space_drag_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.click_modifier_right = input + .get_digital_action_data(self.data.click_modifier_right_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.click_modifier_middle = input + .get_digital_action_data(self.data.click_modifier_middle_hnd, hand.data.input_hnd) + .map(|x| x.0.bState) + .unwrap_or(false); + + hand.now.scroll = input + .get_analog_action_data(self.data.scroll_hnd, hand.data.input_hnd) + .map(|x| x.0.x) + .unwrap_or(0.0); + } + + let devices = system.get_device_to_absolute_tracking_pose(universe, 0.005); + copy_from_hmd(&devices[0].mDeviceToAbsoluteTracking, &mut self.hmd); + } + + pub fn update_devices(&mut self, system: &mut SystemManager) { + self.devices.clear(); + for i in 0..k_unMaxTrackedDeviceCount { + let index = TrackedDeviceIndex(i); + let maybe_role = match system.get_tracked_device_class(index) { + ETrackedDeviceClass::TrackedDeviceClass_HMD => Some(TrackedDeviceRole::Hmd), + ETrackedDeviceClass::TrackedDeviceClass_Controller => { + let sys_role = system.get_controller_role_for_tracked_device_index(index); + match sys_role { + ETrackedControllerRole::TrackedControllerRole_LeftHand => { + Some(TrackedDeviceRole::LeftHand) + } + ETrackedControllerRole::TrackedControllerRole_RightHand => { + Some(TrackedDeviceRole::RightHand) + } + _ => None, + } + } + ETrackedDeviceClass::TrackedDeviceClass_GenericTracker => { + Some(TrackedDeviceRole::Tracker) + } + _ => None, + }; + + if let Some(role) = maybe_role { + if let Some(device) = get_tracked_device(system, index, role) { + self.devices.push(device); + } + } + } + self.devices.sort_by(|a, b| { + (a.role as u8) + .cmp(&(b.role as u8)) + .then(a.index.0.cmp(&b.index.0)) + }); + } +} + +fn get_tracked_device( + system: &mut SystemManager, + index: TrackedDeviceIndex, + role: TrackedDeviceRole, +) -> Option { + let soc = system + .get_tracked_device_property( + index, + ETrackedDeviceProperty::Prop_DeviceBatteryPercentage_Float, + ) + .ok(); + + let charging = if soc.is_some() { + system + .get_tracked_device_property(index, ETrackedDeviceProperty::Prop_DeviceIsCharging_Bool) + .unwrap_or(false) + } else { + false + }; + + Some(TrackedDevice { + valid: true, + index, + soc, + charging, + role, + }) +} + +fn copy_from_hmd(in_mat: &HmdMatrix34_t, out_mat: &mut Affine3A) { + out_mat.x_axis[0] = in_mat.m[0][0]; + out_mat.x_axis[1] = in_mat.m[1][0]; + out_mat.x_axis[2] = in_mat.m[2][0]; + out_mat.y_axis[0] = in_mat.m[0][1]; + out_mat.y_axis[1] = in_mat.m[1][1]; + out_mat.y_axis[2] = in_mat.m[2][1]; + out_mat.z_axis[0] = in_mat.m[0][2]; + out_mat.z_axis[1] = in_mat.m[1][2]; + out_mat.z_axis[2] = in_mat.m[2][2]; + out_mat.w_axis[0] = in_mat.m[0][3]; + out_mat.w_axis[1] = in_mat.m[1][3]; + out_mat.w_axis[2] = in_mat.m[2][3]; +} diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs new file mode 100644 index 0000000..b2aa43d --- /dev/null +++ b/src/backend/openvr/mod.rs @@ -0,0 +1,100 @@ +use std::{ + path::Path, + time::{Duration, Instant}, +}; + +use log::{error, info}; +use ovr_overlay::{ + sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType}, + TrackedDeviceIndex, +}; + +use super::common::InputState; + +pub mod input; +pub mod overlay; + +fn openvr_run() { + let app_type = EVRApplicationType::VRApplication_Overlay; + let Ok(context) = ovr_overlay::Context::init(app_type) else { + error!("Failed to initialize OpenVR"); + return; + }; + + let mut overlay = context.overlay_mngr(); + let mut settings = context.settings_mngr(); + let mut input = context.input_mngr(); + let mut system = context.system_mngr(); + let mut compositor = context.compositor_mngr(); + + let Ok(_) = input.set_action_manifest(Path::new("resources/actions.json")) else { + error!("Failed to set action manifest"); + return; + }; + + let Ok(mut input_state) = InputState::new(&mut input) else { + error!("Failed to initialize input"); + return; + }; + + let Ok(refresh_rate) = system.get_tracked_device_property::(TrackedDeviceIndex::HMD, ETrackedDeviceProperty::Prop_DisplayFrequency_Float) else { + error!("Failed to get display refresh rate"); + return; + }; + + let frame_time = (1000.0 / refresh_rate).floor() * 0.001; + let mut next_device_update = Instant::now(); + + loop { + while let Some(event) = system.poll_next_event() { + match event.event_type { + EVREventType::VREvent_Quit => { + info!("Received quit event, shutting down."); + return; + } + EVREventType::VREvent_TrackedDeviceActivated + | EVREventType::VREvent_TrackedDeviceDeactivated + | EVREventType::VREvent_TrackedDeviceUpdated => { + next_device_update = Instant::now(); + } + _ => {} + } + + if next_device_update <= Instant::now() { + input_state.update_devices(&mut system); + next_device_update = Instant::now() + Duration::from_secs(30); + } + + input_state.pre_update(); + input_state.update(&mut input, &mut system); + input_state.post_update(); + + // task scheduler + + // after input + + // interactions + + // show overlays + + // chaperone + + // render overlays + + // hide overlays + + // close font handles + + // playspace moved end frame + + let mut seconds_since_vsync = 0f32; + std::thread::sleep(Duration::from_secs_f32( + if system.get_time_since_last_vsync(&mut seconds_since_vsync, &mut 0u64) { + frame_time - seconds_since_vsync + } else { + 0.011 + }, + )); + } + } +} diff --git a/src/backend/openvr/overlay.rs b/src/backend/openvr/overlay.rs new file mode 100644 index 0000000..85ba9e4 --- /dev/null +++ b/src/backend/openvr/overlay.rs @@ -0,0 +1,15 @@ +use ovr_overlay::sys::VRVulkanTextureData_t; + +use crate::overlays::OverlayData; + +pub(super) struct OpenVrOverlayManager { + pub(super) overlays: Vec, +} + +pub(super) struct OpenVrOverlay { + pub(super) visible: bool, + pub(super) color: [f32; 4], + overlay: OverlayData, + handle: u32, + ovr_texture: VRVulkanTextureData_t, +} diff --git a/src/backend/openxr.rs b/src/backend/openxr.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..725b3a3 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,559 @@ +use std::{sync::Arc, slice::Iter, io::Cursor, error::Error}; + +use log::{info,error}; +use vulkano::{format::Format, device::{physical::PhysicalDeviceType, QueueFlags, DeviceExtensions, Device, DeviceCreateInfo, Features, QueueCreateInfo, Queue}, Version, VulkanLibrary, instance::{Instance, InstanceCreateInfo}, memory::allocator::{StandardMemoryAllocator, AllocationCreateInfo, MemoryUsage}, command_buffer::{allocator::StandardCommandBufferAllocator, CommandBufferUsage, AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, RenderingAttachmentInfo, RenderingInfo, PrimaryCommandBufferAbstract, CommandBufferExecFuture, SubpassContents, SecondaryAutoCommandBuffer, CommandBufferInheritanceInfo, CommandBufferInheritanceRenderPassType, CommandBufferInheritanceRenderingInfo}, descriptor_set::{allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet}, buffer::{Buffer, BufferCreateInfo, BufferUsage, BufferContents, Subbuffer, allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}}, sampler::{Filter, SamplerCreateInfo, SamplerAddressMode, Sampler}, pipeline::{GraphicsPipeline, Pipeline, graphics::{render_pass::PipelineRenderingCreateInfo, vertex_input::Vertex, input_assembly::InputAssemblyState, viewport::{ViewportState, Viewport}, color_blend::{ColorBlendState, AttachmentBlend}}, PipelineBindPoint}, image::{ImageViewAbstract, ImageUsage, SwapchainImage, ImageDimensions, ImmutableImage, MipmapsCount, StorageImage, ImageError, SubresourceData, ImageCreateFlags, AttachmentImage}, swapchain::{Surface, Swapchain, SwapchainCreateInfo, CompositeAlpha}, shader::ShaderModule, render_pass::{StoreOp, LoadOp}, sync::future::NowFuture}; +use winit::{event_loop::EventLoop, window::{WindowBuilder, Window}}; +use vulkano_win::VkSurfaceBuild; +use wlx_capture::frame::{DmabufFrame, DRM_FORMAT_ABGR8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888}; + +#[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 struct WlxGraphics { + pub instance: Arc, + pub device: Arc, + pub queue: Arc, + + pub surface: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, + + pub quad_indices: Subbuffer<[u16]>, +} + +impl WlxGraphics { + pub fn new() -> (Arc, EventLoop<()>) { + #[cfg(debug_assertions)] + let layers = vec!["VK_LAYER_KHRONOS_validation".to_owned()]; + #[cfg(not(debug_assertions))] + let layers = vec![]; + + let library = VulkanLibrary::new().unwrap(); + let required_extensions = vulkano_win::required_extensions(&library); + + let instance = Instance::new( + library, + InstanceCreateInfo { + enabled_extensions: required_extensions, + enabled_layers: layers, + enumerate_portability: true, + ..Default::default() + }, + ) + .unwrap(); + + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + khr_external_memory: true, + khr_external_memory_fd: true, + ext_external_memory_dma_buf: true, + ext_image_drm_format_modifier: true, + ..DeviceExtensions::empty() + }; + + // TODO headless + let event_loop = EventLoop::new(); + let surface = WindowBuilder::new() + .build_vk_surface(&event_loop, instance.clone()) + .unwrap(); + + + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| { + p.supported_extensions().contains(&device_extensions) + }) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.surface_support(i as u32, &surface).unwrap_or(false) + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| { + match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + } + }) + .expect("no suitable physical device found"); + + info!( + "Nice {} you have there.", + physical_device.properties().device_name, + ); + + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + enabled_features: Features { + dynamic_rendering: true, + ..Features::empty() + }, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(device.clone(), Default::default())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(device.clone())); + + let quad_indices = Buffer::from_iter( + &memory_allocator, + BufferCreateInfo { + usage: BufferUsage::INDEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + usage: MemoryUsage::Upload, + ..Default::default() + }, + INDICES.iter().cloned(), + ).unwrap(); + + let me = Self { + instance, + device, + queue, + surface, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + quad_indices, + }; + + (Arc::new(me), event_loop) + } + + pub fn create_swapchain(&self, format: Option) -> (Arc, Vec>) { + let (min_image_count, composite_alpha, image_format) = if let Some(format) = format { + (1, CompositeAlpha::Opaque, format) + } else { + let surface_capabilities = self.device + .physical_device() + .surface_capabilities(&self.surface, Default::default()) + .unwrap(); + + let composite_alpha = surface_capabilities.supported_composite_alpha.into_iter().next().unwrap(); + + let image_format = Some( + self.device + .physical_device() + .surface_formats(&self.surface, Default::default()) + .unwrap()[0] + .0, + ); + (surface_capabilities.min_image_count, composite_alpha, image_format.unwrap()) + }; + let window = self.surface.object().unwrap().downcast_ref::().unwrap(); + let swapchain = Swapchain::new( + self.device.clone(), + self.surface.clone(), + SwapchainCreateInfo { + min_image_count, + image_format: Some(image_format), + image_extent: window.inner_size().into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha, + ..Default::default() + }, + ) + .unwrap(); + + swapchain + } + + pub fn upload_verts(&self, width: f32, height: f32, x: f32, y: f32, w: f32, h: f32) -> Subbuffer<[Vert2Uv]> { + 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>) -> Subbuffer<[T]> + where T: BufferContents + Clone { + Buffer::from_iter( + &self.memory_allocator, + BufferCreateInfo { + usage, + ..Default::default() + }, + AllocationCreateInfo { + usage: MemoryUsage::Upload, + ..Default::default() + }, + contents.cloned(), + ).unwrap() + } + + pub fn dmabuf_texture(&self, frame: DmabufFrame) -> Result, ImageError> { + let dimensions = ImageDimensions::Dim2d { + width: frame.format.width, + height: frame.format.height, + array_layers: 1, + }; + + let format = match frame.format.fourcc { + DRM_FORMAT_ABGR8888 => Format::R8G8B8A8_UNORM, + DRM_FORMAT_XBGR8888 => Format::R8G8B8A8_UNORM, + DRM_FORMAT_ARGB8888 => Format::B8G8R8A8_UNORM, + DRM_FORMAT_XRGB8888 => Format::B8G8R8A8_UNORM, + _ => panic!("Unsupported dmabuf format {:x}", frame.format.fourcc), + }; + + let planes = frame.planes + .iter() + .take(frame.num_planes) + .filter_map(|plane| { + let Some(fd) = plane.fd else { + return None; + }; + Some(SubresourceData { + fd, + offset: plane.offset as _, + row_pitch: plane.stride as _, + }) + }).collect(); + + StorageImage::new_from_dma_buf_fd( + &self.memory_allocator, + self.device.clone(), + dimensions, + format, + ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC, + ImageCreateFlags::empty(), + [self.queue.queue_family_index()], + planes, + frame.format.modifier, + ) + } + pub fn render_texture(&self, width: u32, height: u32, format: Format) -> Arc { + let tex = AttachmentImage::with_usage( + &self.memory_allocator, + [width, height], + format, + ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC | ImageUsage::COLOR_ATTACHMENT, + ).unwrap(); + + tex + } + pub fn create_pipeline(self: &Arc, vert: Arc, frag: Arc, format: Format) -> Arc { + Arc::new(WlxPipeline::new(self.clone(), vert, frag, format)) + } + pub fn create_command_buffer(self: &Arc, usage: CommandBufferUsage) -> WlxCommandBuffer { + let command_buffer = AutoCommandBufferBuilder::primary( + &self.command_buffer_allocator, + self.queue.queue_family_index(), + usage, + ).unwrap(); + WlxCommandBuffer { graphics: self.clone(), command_buffer } + } + +} + +pub struct WlxCommandBuffer { + graphics: Arc, + command_buffer: AutoCommandBufferBuilder>, +} + +impl WlxCommandBuffer { + pub fn inner(&self) -> &AutoCommandBufferBuilder> { + &self.command_buffer + } + + pub fn inner_mut(&mut self) -> &mut AutoCommandBufferBuilder> { + &mut self.command_buffer + } + + pub fn to_inner(self) -> AutoCommandBufferBuilder> { + self.command_buffer + } + + pub fn begin(mut self, render_target: Arc) -> Self + { + self.command_buffer + .begin_rendering(RenderingInfo { + contents: SubpassContents::SecondaryCommandBuffers, + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: LoadOp::Clear, + store_op: StoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), + ..RenderingAttachmentInfo::image_view( + render_target.clone(), + ) + })], + ..Default::default() + }).unwrap(); + self + } + + pub fn run_ref(&mut self, pass: &WlxPass) -> &mut Self + { + let _ = self.command_buffer.execute_commands(pass.command_buffer.clone()).unwrap(); + self + } + + pub fn run(mut self, pass: &WlxPass) -> Self + { + let _ = self.command_buffer.execute_commands(pass.command_buffer.clone()); + self + } + + pub fn texture2d(&mut self, width: u32, height: u32, format: Format, data: Vec) -> Arc { + let dimensions = ImageDimensions::Dim2d { + width, + height, + array_layers: 1, + }; + + ImmutableImage::from_iter( + &self.graphics.memory_allocator, + data, + dimensions, + MipmapsCount::One, + format, + &mut self.command_buffer, + ) + .unwrap() + } + + pub fn texture2d_png(&mut self, bytes: Vec) -> Arc { + let cursor = Cursor::new(bytes); + let decoder = png::Decoder::new(cursor); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let width = info.width; + let height = info.height; + let mut image_data = Vec::new(); + image_data.resize((info.width * info.height * 4) as usize, 0); + reader.next_frame(&mut image_data).unwrap(); + self.texture2d(width, height, Format::R8G8B8A8_UNORM, image_data) + } + +} + +impl WlxCommandBuffer { + pub fn end_render_and_continue(&mut self) { + self.command_buffer.end_rendering().unwrap(); + } + + pub fn end_render(self) -> PrimaryAutoCommandBuffer { + let mut buf = self.command_buffer; + buf.end_rendering().unwrap(); + + buf.build().unwrap() + } + + pub fn end(self) -> PrimaryAutoCommandBuffer { + self.command_buffer.build().unwrap() + } + + pub fn end_render_and_execute(self) -> CommandBufferExecFuture { + let mut buf = self.command_buffer; + buf.end_rendering().unwrap(); + let buf = buf.build().unwrap(); + buf.execute(self.graphics.queue.clone()).unwrap() + } + + pub fn end_and_execute(self) -> CommandBufferExecFuture { + let buf = self.command_buffer; + let buf = buf.build().unwrap(); + buf.execute(self.graphics.queue.clone()).unwrap() + } +} + +pub struct WlxPipeline { + pub graphics: Arc, + pub pipeline: Arc, + pub format: Format, +} + +impl WlxPipeline { + fn new(graphics: Arc, vert: Arc, frag: Arc, format: Format) -> Self { + let vep = vert.entry_point("main").unwrap(); + let fep = frag.entry_point("main").unwrap(); + let pipeline = GraphicsPipeline::start() + .render_pass(PipelineRenderingCreateInfo { + color_attachment_formats: vec![Some(format)], + ..Default::default() + }) + .color_blend_state(ColorBlendState::default().blend( + AttachmentBlend::alpha() + )) + .vertex_input_state(Vert2Uv::per_vertex()) + .input_assembly_state(InputAssemblyState::new()) + .vertex_shader(vep, ()) + .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) + .fragment_shader(fep, ()) + .build(graphics.device.clone()) + .unwrap(); + + Self { graphics, pipeline, format} + } + + pub fn inner(&self) -> Arc { + self.pipeline.clone() + } + + pub fn graphics(&self) -> Arc { + self.graphics.clone() + } + + pub fn uniform_sampler(&self, set: usize, texture: Arc, filter: Filter) -> Arc { + let sampler = Sampler::new( + self.graphics.device.clone(), + SamplerCreateInfo { + mag_filter: filter, + min_filter: filter, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + + let layout = self.pipeline.layout().set_layouts().get(set).unwrap(); + + PersistentDescriptorSet::new( + &self.graphics.descriptor_set_allocator, + layout.clone(), + [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], + ) + .unwrap() + } + + pub fn uniform_buffer(&self, set: usize, data: Vec) -> Arc + where T: BufferContents + Copy { + let uniform_buffer = SubbufferAllocator::new( + self.graphics.memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() + }, + ); + + let uniform_buffer_subbuffer = { + let subbuffer = uniform_buffer.allocate_slice(data.len() as _).unwrap(); + subbuffer.write().unwrap().copy_from_slice(data.as_slice()); + subbuffer + }; + + let layout = self.pipeline.layout().set_layouts().get(set).unwrap(); + PersistentDescriptorSet::new( + &self.graphics.descriptor_set_allocator, + layout.clone(), + [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)] + ).unwrap() + } + + pub fn create_pass(self: &Arc, dimensions: [f32; 2], vertex_buffer: Subbuffer<[Vert2Uv]>, index_buffer: Subbuffer<[u16]>, descriptor_sets: Vec>) -> WlxPass { + WlxPass::new(self.clone(), dimensions, vertex_buffer, index_buffer, descriptor_sets) + } +} + +pub struct WlxPass +{ + pipeline: Arc, + vertex_buffer: Subbuffer<[Vert2Uv]>, + index_buffer: Subbuffer<[u16]>, + descriptor_sets: Vec>, + pub command_buffer: Arc, +} + +impl WlxPass +{ + fn new(pipeline: Arc, dimensions: [f32; 2], vertex_buffer: Subbuffer<[Vert2Uv]>, index_buffer: Subbuffer<[u16]>, descriptor_sets: Vec>) -> Self { + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0..1.0, + }; + + let pipeline_inner = pipeline.inner().clone(); + let mut command_buffer = AutoCommandBufferBuilder::secondary( + &pipeline.graphics.command_buffer_allocator, + pipeline.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() + } + ) + .unwrap(); + + command_buffer.set_viewport(0, [viewport]) + .bind_pipeline_graphics(pipeline_inner) + .bind_descriptor_sets( + PipelineBindPoint::Graphics, + pipeline.inner().layout().clone(), + 0, + descriptor_sets.clone(), + ) + .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_index_buffer(index_buffer.clone()) + .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0) + .or_else(|err| { + if let Some(source) = err.source() { + error!("Failed to draw: {}", source); + } + Err(err) + }).unwrap(); + + Self { + pipeline, + vertex_buffer, + index_buffer, + descriptor_sets, + command_buffer: Arc::new(command_buffer.build().unwrap()), + } + } +} + diff --git a/src/graphics.rs.bak2 b/src/graphics.rs.bak2 new file mode 100644 index 0000000..c2be221 --- /dev/null +++ b/src/graphics.rs.bak2 @@ -0,0 +1,1197 @@ +use core::slice; +use std::{sync::Arc, slice::Iter, io::Cursor, error::Error, borrow::{Cow, BorrowMut}, cell::RefCell, fs::File, os::fd::{FromRawFd, IntoRawFd}, mem}; + +use ash::{extensions::{ + ext::DebugUtils, + khr::{Surface,Swapchain, self}, +}, util::Align}; + +use ash::vk; +use cstr::cstr; +use glam::f32::Vec4; +use libc::c_char; +use log::{info,error}; +use raw_window_handle::*; +use smallvec::smallvec; +use std::env; +use std::ffi::CStr; +use winit::{event_loop::{EventLoop, self, ControlFlow}, window::{WindowBuilder, Window}, event::{WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, Event}, platform::run_return::EventLoopExtRunReturn}; +use wlx_capture::frame::{DmabufFrame, DRM_FORMAT_ABGR8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888}; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct Vertex { + pub in_pos: Vec4, + pub in_uv: Vec4, +} + +const APP_NAME: &str = env!("CARGO_PKG_NAME"); +pub const INDICES : [u16; 6] = [2, 1, 0, 1, 2, 3]; + +pub const VERTICES : [Vertex; 4] = [ + Vertex{ in_pos: Vec4::new(0.0, 0.0, 0.0, 0.0), in_uv: Vec4::new(0.0, 0.0, 0.0, 0.0) }, + Vertex{ in_pos: Vec4::new(0.0, 1.0, 0.0, 0.0), in_uv: Vec4::new(0.0, 1.0, 0.0, 0.0) }, + Vertex{ in_pos: Vec4::new(1.0, 0.0, 0.0, 0.0), in_uv: Vec4::new(1.0, 0.0, 0.0, 0.0) }, + Vertex{ in_pos: Vec4::new(1.0, 1.0, 0.0, 0.0), in_uv: Vec4::new(1.0, 1.0, 0.0, 0.0) }, + ]; + +// Simple offset_of macro akin to C++ offsetof +#[macro_export] +macro_rules! offset_of { + ($base:path, $field:ident) => {{ + #[allow(unused_unsafe)] + unsafe { + let b: $base = mem::zeroed(); + std::ptr::addr_of!(b.$field) as isize - std::ptr::addr_of!(b) as isize + } + }}; +} + +pub struct VkGraphics { + pub event_loop: RefCell>, + pub entry: ash::Entry, + + pub instance: ash::Instance, + pub device: ash::Device, + + pub surface_loader: Surface, + pub swapchain_loader: Swapchain, + pub window: winit::window::Window, + + pub device_properies: vk::PhysicalDeviceProperties, + pub device_memory_properties: vk::PhysicalDeviceMemoryProperties, + pub queue_family_index: u32, + pub present_queue: vk::Queue, + + pub surface: vk::SurfaceKHR, + pub surface_format: vk::SurfaceFormatKHR, + pub surface_resolution: vk::Extent2D, + + pub swapchain: vk::SwapchainKHR, + pub swapchain_format: vk::Format, + pub present_images: Vec, + pub present_image_views: Vec, + + pub pool: vk::CommandPool, + pub draw_command_buffer: vk::CommandBuffer, + pub setup_command_buffer: vk::CommandBuffer, + + pub draw_commands_reuse_fence: vk::Fence, + pub setup_commands_reuse_fence: vk::Fence, + + #[cfg(debug_assertions)] + pub debug_utils_loader: DebugUtils, + #[cfg(debug_assertions)] + pub debug_call_back: vk::DebugUtilsMessengerEXT, +} + +impl VkGraphics { + pub fn new() -> Arc { + unsafe { + let window_size = [1920, 1080]; + + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .with_title("Vulkan") + .with_inner_size(winit::dpi::LogicalSize::new( + window_size[0], + window_size[1], + )) + .build(&event_loop) + .unwrap(); + + let entry = ash::Entry::load().unwrap(); + let app_name = cstr!(APP_NAME); + let layer_names = [ + #[cfg(debug_assertions)] + cstr!("VK_LAYER_KHRONOS_validation"), + ]; + let layers_names_raw: Vec<*const c_char> = layer_names + .iter() + .map(|raw_name| raw_name.as_ptr()) + .collect(); + + let mut extension_names = ash_window::enumerate_required_extensions(window.raw_display_handle()) + .unwrap() + .to_vec(); + + #[cfg(debug_assertions)] + extension_names.push(DebugUtils::name().as_ptr()); + + let appinfo = vk::ApplicationInfo::builder() + .application_name(app_name) + .application_version(0) + .engine_name(app_name) + .engine_version(0) + .api_version(vk::make_api_version(0, 1, 3, 0)); + + let create_info = vk::InstanceCreateInfo::builder() + .application_info(&appinfo) + .enabled_layer_names(&layers_names_raw) + .enabled_extension_names(&extension_names) + .flags(vk::InstanceCreateFlags::default()); + + let instance: ash::Instance = entry + .create_instance(&create_info, None) + .expect("Could not create Vulkan instance."); + + #[cfg(debug_assertions)] + let (debug_utils_loader, debug_call_back) = + { + let loader = DebugUtils::new(&entry, &instance); + let info = vk::DebugUtilsMessengerCreateInfoEXT::builder() + .message_severity( + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR + | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING + | vk::DebugUtilsMessageSeverityFlagsEXT::INFO + | vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE, + ) + .message_type( + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, + ) + .pfn_user_callback(Some(vulkan_debug_callback)) + .build(); + + let callback = loader + .create_debug_utils_messenger(&info, None) + .unwrap(); + (loader, callback) + }; + + let surface = ash_window::create_surface( + &entry, + &instance, + window.raw_display_handle(), + window.raw_window_handle(), + None, + ) + .unwrap(); + + let surface_loader = Surface::new(&entry, &instance); + + let pdevices = instance + .enumerate_physical_devices() + .expect("Physical device error"); + + let (pdevice, queue_family_index) = pdevices + .iter() + .find_map(|pdevice| { + instance + .get_physical_device_queue_family_properties(*pdevice) + .iter() + .enumerate() + .find_map(|(index, info)| { + let supports_graphic_and_surface = + info.queue_flags.contains(vk::QueueFlags::GRAPHICS) + && surface_loader + .get_physical_device_surface_support( + *pdevice, + index as u32, + surface, + ) + .unwrap(); + if supports_graphic_and_surface { + Some((*pdevice, index)) + } else { + None + } + }) + }) + .expect("Couldn't find suitable device."); + + let queue_family_index = queue_family_index as u32; + let device_extension_names_raw = [ + Swapchain::name().as_ptr(), + vk::KhrExternalMemoryFn::name().as_ptr(), + vk::KhrExternalMemoryFdFn::name().as_ptr(), + vk::ExtExternalMemoryDmaBufFn::name().as_ptr(), + vk::ExtImageDrmFormatModifierFn::name().as_ptr(), + ]; + + let priorities = [1.0]; + + let queue_info = vk::DeviceQueueCreateInfo::builder() + .queue_family_index(queue_family_index) + .queue_priorities(&priorities); + + let physical_device_features = vk::PhysicalDeviceFeatures::builder().shader_clip_distance(true).build(); + let mut physical_device_vulkan_13_features = vk::PhysicalDeviceVulkan13Features::builder().dynamic_rendering(true).synchronization2(true).build(); + + let mut features = vk::PhysicalDeviceFeatures2::builder() + .features(physical_device_features) + .push_next(&mut physical_device_vulkan_13_features) + .build(); + + let device_create_info = vk::DeviceCreateInfo::builder() + .queue_create_infos(std::slice::from_ref(&queue_info)) + .enabled_extension_names(&device_extension_names_raw) + .push_next(&mut features); + + let device: ash::Device = instance + .create_device(pdevice, &device_create_info, None) + .unwrap(); + + let present_queue = device.get_device_queue(queue_family_index, 0); + + let surface_format = surface_loader + .get_physical_device_surface_formats(pdevice, surface) + .unwrap()[0]; + + let surface_capabilities = surface_loader + .get_physical_device_surface_capabilities(pdevice, surface) + .unwrap(); + + let desired_image_count = surface_capabilities.min_image_count; + let surface_resolution = match surface_capabilities.current_extent.width { + std::u32::MAX => vk::Extent2D { + width: window_size[0], + height: window_size[1], + }, + _ => surface_capabilities.current_extent, + }; + + let pre_transform = if surface_capabilities + .supported_transforms + .contains(vk::SurfaceTransformFlagsKHR::IDENTITY) + { + vk::SurfaceTransformFlagsKHR::IDENTITY + } else { + surface_capabilities.current_transform + }; + let present_modes = surface_loader + .get_physical_device_surface_present_modes(pdevice, surface) + .unwrap(); + let present_mode = present_modes + .iter() + .cloned() + .find(|&mode| mode == vk::PresentModeKHR::MAILBOX) + .unwrap_or(vk::PresentModeKHR::FIFO); + + let swapchain_loader = Swapchain::new(&instance, &device); + + #[allow(unused)] + let dmabuf_loader = vk::ExtImageDrmFormatModifierFn::load(|name| { + std::mem::transmute(instance.get_device_proc_addr(device.handle(), name.as_ptr())) + }); + + let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder() + .surface(surface) + .min_image_count(desired_image_count) + .image_color_space(surface_format.color_space) + .image_format(surface_format.format) + .image_extent(surface_resolution) + .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .pre_transform(pre_transform) + .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) + .present_mode(present_mode) + .clipped(true) + .image_array_layers(1); + + let swapchain = swapchain_loader + .create_swapchain(&swapchain_create_info, None) + .unwrap(); + + let pool_create_info = vk::CommandPoolCreateInfo::builder() + .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) + .queue_family_index(queue_family_index); + + let pool = device.create_command_pool(&pool_create_info, None).unwrap(); + + let command_buffer_allocate_info = vk::CommandBufferAllocateInfo::builder() + .command_buffer_count(2) + .command_pool(pool) + .level(vk::CommandBufferLevel::PRIMARY); + + let command_buffers = device + .allocate_command_buffers(&command_buffer_allocate_info) + .unwrap(); + + let setup_command_buffer = command_buffers[0]; + let draw_command_buffer = command_buffers[1]; + + let present_images = swapchain_loader.get_swapchain_images(swapchain).unwrap(); + let present_image_views: Vec = present_images + .iter() + .map(|&image| { + let create_view_info = vk::ImageViewCreateInfo::builder() + .view_type(vk::ImageViewType::TYPE_2D) + .format(surface_format.format) + .components(vk::ComponentMapping { + r: vk::ComponentSwizzle::R, + g: vk::ComponentSwizzle::G, + b: vk::ComponentSwizzle::B, + a: vk::ComponentSwizzle::A, + }) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }) + .image(image); + device.create_image_view(&create_view_info, None).unwrap() + }) + .collect(); + let device_memory_properties = instance.get_physical_device_memory_properties(pdevice); + + let device_properies = instance.get_physical_device_properties(pdevice); + + let fence_create_info = + vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED); + + let draw_commands_reuse_fence = device + .create_fence(&fence_create_info, None) + .expect("Create fence failed."); + let setup_commands_reuse_fence = device + .create_fence(&fence_create_info, None) + .expect("Create fence failed."); + + let me = Self { + event_loop: RefCell::new(event_loop), + entry, + instance, + device, + queue_family_index, + device_properies, + device_memory_properties, + window, + surface_loader, + surface_format, + present_queue, + surface_resolution, + swapchain_loader, + swapchain, + swapchain_format: surface_format.format, + present_images, + present_image_views, + pool, + draw_command_buffer, + setup_command_buffer, + draw_commands_reuse_fence, + setup_commands_reuse_fence, + surface, + #[cfg(debug_assertions)] + debug_call_back, + #[cfg(debug_assertions)] + debug_utils_loader, + }; + Arc::new(me) + } + } + + pub fn render_loop(&self, f: F) { + self.event_loop + .borrow_mut() + .run_return(|event, _, control_flow| { + *control_flow = ControlFlow::Poll; + match event { + Event::WindowEvent { + event: + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + }, + .. + } => *control_flow = ControlFlow::Exit, + Event::MainEventsCleared => f(), + _ => (), + } + }); + } + + pub fn create_frame(self: &Arc) -> VkFrame { + VkFrame::new(self.clone()) + } + + pub fn create_shader(self: &Arc, spv_bytes: &[u8], descriptor_types: Option>) -> Arc { + VkShader::new(self.clone(), spv_bytes, descriptor_types) + } + + pub fn create_pipeline(self: &Arc, vert: Arc, frag: Arc, format: vk::Format) -> VkPipeline { + VkPipeline::new(self.clone(), vert, frag, format) + } + + pub fn create_buffer(self: &Arc, usage: vk::BufferUsageFlags, data: &[T]) -> Arc + where T: Copy { + VkBuffer::new(self.clone(), usage, data) + } +} + +impl Drop for VkGraphics { + fn drop(&mut self) { + unsafe { + self.device.device_wait_idle().unwrap(); + self.device + .destroy_fence(self.draw_commands_reuse_fence, None); + self.device + .destroy_fence(self.setup_commands_reuse_fence, None); + for &image_view in self.present_image_views.iter() { + self.device.destroy_image_view(image_view, None); + } + self.device.destroy_command_pool(self.pool, None); + self.swapchain_loader + .destroy_swapchain(self.swapchain, None); + self.surface_loader.destroy_surface(self.surface, None); + self.debug_utils_loader + .destroy_debug_utils_messenger(self.debug_call_back, None); + self.instance.destroy_instance(None); + self.device.destroy_device(None); + } + } +} + +pub struct VkFrame { + pub graphics: Arc, + + pub command_pool: vk::CommandPool, + pub command_buffer: vk::CommandBuffer, + + pub present_semaphore: vk::Semaphore, + pub render_semaphore: vk::Semaphore, + + pub fence: vk::Fence, +} + +impl VkFrame { + pub fn new(graphics: Arc) -> Self { + let command_pool = unsafe { graphics.device.create_command_pool(&vk::CommandPoolCreateInfo::default(), None) }.unwrap(); + let command_buffer = unsafe { graphics.device.allocate_command_buffers(&vk::CommandBufferAllocateInfo::builder().command_pool(command_pool).command_buffer_count(1).build()) }.unwrap()[0]; + let present_semaphore = unsafe { graphics.device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None) }.unwrap(); + let render_semaphore = unsafe { graphics.device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None) }.unwrap(); + let fence = unsafe { graphics.device.create_fence(&vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED).build(), None) }.unwrap(); + + Self { + graphics, + command_pool, + command_buffer, + present_semaphore, + render_semaphore, + fence, + } + } +} + +impl Drop for VkFrame { + fn drop(&mut self) { + unsafe { + self.graphics.device.destroy_command_pool(self.command_pool, None); + self.graphics + .device + .destroy_semaphore(self.present_semaphore, None); + self.graphics + .device + .destroy_semaphore(self.render_semaphore, None); + self.graphics.device.destroy_fence(self.fence, None); + } + } +} + +pub struct VkBuffer { + pub graphics: Arc, + pub buffer: vk::Buffer, + pub memory: vk::DeviceMemory, +} + +impl VkBuffer +{ + pub fn new(graphics: Arc, usage: vk::BufferUsageFlags, data: &[T]) -> Arc + where T: Copy + { + unsafe { + let buffer_size = std::mem::size_of::() * data.len(); + + let buffer_create_info = vk::BufferCreateInfo::builder() + .size(buffer_size as _) + .usage(usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + let buffer = graphics.device.create_buffer(&buffer_create_info, None).unwrap(); + + let memory_req = graphics.device.get_buffer_memory_requirements(buffer); + + let memory_index = find_memorytype_index( + &memory_req, + &graphics.device_memory_properties, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + ) + .expect("Unable to find suitable memory index for vertex buffer."); + + let memory_allocate_info = vk::MemoryAllocateInfo::builder() + .allocation_size(memory_req.size) + .memory_type_index(memory_index) + .build(); + + let memory = graphics + .device + .allocate_memory(&memory_allocate_info, None) + .unwrap(); + + let memory_ptr = graphics + .device + .map_memory(memory, 0, memory_req.size, vk::MemoryMapFlags::empty()) + .unwrap(); + + let mut slice = Align::::new(memory_ptr, std::mem::align_of::() as _, memory_req.size); + slice.copy_from_slice(data); + + graphics.device.unmap_memory(memory); + graphics.device.bind_buffer_memory(buffer, memory, 0).unwrap(); + + Arc::new(Self { + graphics, + buffer, + memory, + }) + } + } +} + +impl Drop for VkBuffer{ + fn drop(&mut self) { + unsafe { + self.graphics.device.free_memory(self.memory, None); + self.graphics.device.destroy_buffer(self.buffer, None); + } + } +} + +pub struct VkImage { + pub graphics: Arc, + pub image: vk::Image, + pub memory: vk::DeviceMemory, + pub view: Option, + pub sampler: Option, + pub buffer: Option>, +} + +impl VkImage { + pub fn new_empty(graphics: Arc, width: u32, height: u32) -> Self { + todo!(); + } + + pub fn new_from_bytes(&self, graphics: Arc, width: u32, height: u32, format: vk::Format, data: &[u8]) -> Self { + let image_extent = vk::Extent3D { width, height, depth: 1 }; + let image = { + let image_create_info = vk::ImageCreateInfo::builder() + .image_type(vk::ImageType::TYPE_2D) + .format(vk::Format::R8G8B8A8_UNORM) + .extent(image_extent) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(vk::ImageUsageFlags::TRANSFER_DST + | vk::ImageUsageFlags::TRANSFER_SRC + | vk::ImageUsageFlags::SAMPLED) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + unsafe { graphics.device.create_image(&image_create_info, None).unwrap() } + }; + + let image_buffer = VkBuffer::new(graphics.clone(), vk::BufferUsageFlags::TRANSFER_SRC, data); + let memory_requirements = unsafe { graphics.device.get_image_memory_requirements(image) }; + let memory_type_index = find_memorytype_index( + &memory_requirements, + &graphics.device_memory_properties, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + ).unwrap(); + + let memory = { + let alloc_info = vk::MemoryAllocateInfo::builder() + .allocation_size(memory_requirements.size) + .memory_type_index(memory_type_index) + .build(); + + unsafe { graphics.device.allocate_memory(&alloc_info, None).unwrap() } + }; + + unsafe { graphics.device.bind_image_memory(image, memory, 0).unwrap() }; + + record_submit_commandbuffer( + &graphics.device, + graphics.setup_command_buffer, + graphics.setup_commands_reuse_fence, + graphics.present_queue, + &[], + &[], + &[], + |device, texture_command_buffer| { + let barrier = vk::ImageMemoryBarrier { + dst_access_mask: vk::AccessFlags::TRANSFER_WRITE, + new_layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL, + image, + subresource_range: vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + level_count: 1, + layer_count: 1, + ..Default::default() + }, + ..Default::default() + }; + + unsafe { device.cmd_pipeline_barrier( + texture_command_buffer, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[], + &[], + &[barrier], + ) }; + + let buffer_copy_regions = vk::BufferImageCopy::builder() + .image_subresource( + vk::ImageSubresourceLayers::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .layer_count(1) + .build() + ) + .image_extent(image_extent) + .build(); + + unsafe { device.cmd_copy_buffer_to_image( + texture_command_buffer, + image_buffer.buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[buffer_copy_regions], + ) }; + + let barrier_end = vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::TRANSFER_WRITE, + dst_access_mask: vk::AccessFlags::SHADER_READ, + old_layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL, + new_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + image, + subresource_range: vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + level_count: 1, + layer_count: 1, + ..Default::default() + }, + ..Default::default() + }; + + unsafe { device.cmd_pipeline_barrier( + texture_command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[], + &[], + &[barrier_end], + ) }; + + }, + ); + + Self { + graphics: graphics.clone(), + image, + memory, + buffer: Some(image_buffer), + view: None, + sampler: None, + } + } + + pub fn new_from_dmabuf(graphics: Arc, frame: &DmabufFrame) -> Self { + debug_assert!(frame.num_planes == 1); + let plane = &frame.planes[0]; + debug_assert!(plane.fd.is_some()); + + let format = match frame.format.fourcc { + DRM_FORMAT_ABGR8888 => vk::Format::R8G8B8A8_UNORM, + DRM_FORMAT_XBGR8888 => vk::Format::R8G8B8A8_UNORM, + DRM_FORMAT_ARGB8888 => vk::Format::B8G8R8A8_UNORM, + DRM_FORMAT_XRGB8888 => vk::Format::B8G8R8A8_UNORM, + _ => panic!("Unsupported dmabuf format {:x}", frame.format.fourcc), + }; + + let image = { + let layout = vk::SubresourceLayout::builder() + .offset(plane.offset as _) + .row_pitch(plane.stride as _) + .size(0) + .array_pitch(0) + .depth_pitch(0) + .build(); + + let mut drm_info = vk::ImageDrmFormatModifierExplicitCreateInfoEXT::builder() + .drm_format_modifier(frame.format.modifier) + .plane_layouts(slice::from_ref(&layout)); + + let mut mem_info = vk::ExternalMemoryImageCreateInfo::builder() + .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); + + let image_create_info = vk::ImageCreateInfo::builder() + .image_type(vk::ImageType::TYPE_2D) + .format(format) + .extent(vk::Extent3D { + width: frame.format.width, + height: frame.format.height, + depth: 1, + }) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT) + .usage(vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::SAMPLED) + .sharing_mode(vk::SharingMode::EXCLUSIVE) + .push_next(&mut drm_info) + .push_next(&mut mem_info) + .build(); + + unsafe { graphics.device.create_image(&image_create_info, None).unwrap() } + }; + + let memory_requirements = unsafe { graphics.device.get_image_memory_requirements(image) }; + let memory_type_index = find_memorytype_index( + &memory_requirements, + &graphics.device_memory_properties, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + ).unwrap(); + + let memory = { + let mut fd_info = vk::ImportMemoryFdInfoKHR::builder() + .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT) + .fd(plane.fd.unwrap()) + .build(); + + let mut ded_req = vk::MemoryDedicatedAllocateInfo::builder() + .image(image) + .build(); + + let alloc_info = vk::MemoryAllocateInfo::builder() + .allocation_size(memory_requirements.size) + .memory_type_index(memory_type_index) + .push_next(&mut fd_info) + .push_next(&mut ded_req) + .build(); + + unsafe { graphics.device.allocate_memory(&alloc_info, None).unwrap() } + }; + + unsafe { graphics.device.bind_image_memory(image, memory, 0).unwrap() }; + + Self { + graphics: graphics.clone(), + image, + memory, + buffer: None, + view: None, + sampler: None, + } + } +} + +impl Drop for VkImage { + fn drop(&mut self) { + unsafe { + if let Some(buffer) = self.buffer.as_ref() { + self.graphics.device.free_memory(buffer.memory, None); + self.graphics.device.destroy_buffer(buffer.buffer, None); + } + if let Some(view) = self.view { + self.graphics.device.destroy_image_view(view, None); + } + self.graphics.device.free_memory(self.memory, None); + self.graphics.device.destroy_image(self.image, None); + } + } +} + +const ENTRY_POINT_MAIN: &'static CStr = cstr!("main"); +pub struct VkShader { + pub graphics: Arc, + pub module: vk::ShaderModule, + pub descriptor_types: Option>, +} + +impl VkShader { + fn new(graphics: Arc, bytes: &[u8], descriptor_types: Option>) -> Arc { + let mut spv = ash::util::read_spv(&mut Cursor::new(bytes)).unwrap(); + let module_create_info = vk::ShaderModuleCreateInfo::builder().code(&mut spv); + let module = unsafe { graphics.device.create_shader_module(&module_create_info, None).unwrap() }; + + Arc::new(Self { + graphics, + module, + descriptor_types, + }) + } +} + +impl Drop for VkShader { + fn drop(&mut self) { + unsafe { + self.graphics.device.destroy_shader_module(self.module, None); + } + } +} + +pub struct VkPipeline { + pub graphics: Arc, + pub pipeline: vk::Pipeline, + pub pipeline_layout: vk::PipelineLayout, + pub desc_set_layouts: Vec, + pub descriptor_pool: vk::DescriptorPool, + pub descriptor_sets: Vec, + pub index_buffer: Arc, + pub vertex_buffer: Arc, +} + +impl VkPipeline { + pub fn new(graphics: Arc, vert: Arc, frag: Arc, format: vk::Format) -> Self { + let shader_stage_create_infos = [ + vk::PipelineShaderStageCreateInfo { + module: vert.module, + p_name: ENTRY_POINT_MAIN.as_ptr(), + stage: vk::ShaderStageFlags::VERTEX, + ..Default::default() + }, + vk::PipelineShaderStageCreateInfo { + module: frag.module, + p_name: ENTRY_POINT_MAIN.as_ptr(), + stage: vk::ShaderStageFlags::FRAGMENT, + ..Default::default() + }, + ]; + + let vertex_input_binding_descriptions = [vk::VertexInputBindingDescription { + binding: 0, + stride: mem::size_of::() as u32, + input_rate: vk::VertexInputRate::VERTEX, + }]; + + let vertex_input_attribute_descriptions = [ + vk::VertexInputAttributeDescription { + location: 0, + binding: 0, + format: vk::Format::R32G32_SFLOAT, + offset: offset_of!(Vertex, in_pos) as u32, + }, + vk::VertexInputAttributeDescription { + location: 1, + binding: 0, + format: vk::Format::R32G32_SFLOAT, + offset: offset_of!(Vertex, in_uv) as u32, + }, + ]; + + let vertex_input_state_info = vk::PipelineVertexInputStateCreateInfo::builder() + .vertex_attribute_descriptions(&vertex_input_attribute_descriptions) + .vertex_binding_descriptions(&vertex_input_binding_descriptions) + .build(); + + let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo::builder() + .topology(vk::PrimitiveTopology::TRIANGLE_LIST) + .build(); + + let desc_layout_bindings = frag.descriptor_types.iter().flatten().enumerate().map(|(i, &desc_type)| { + vk::DescriptorSetLayoutBinding { + binding: i as u32, + descriptor_type: desc_type, + descriptor_count: 1, + stage_flags: vk::ShaderStageFlags::FRAGMENT, + ..Default::default() + } + }).collect::>(); + + let descriptor_info = + vk::DescriptorSetLayoutCreateInfo::builder() + .bindings(&desc_layout_bindings); + + let desc_set_layouts = unsafe { + vec![graphics + .device + .create_descriptor_set_layout(&descriptor_info, None) + .unwrap()] + }; + + let layout_create_info = + vk::PipelineLayoutCreateInfo::builder().set_layouts(&desc_set_layouts); + + let pipeline_layout = unsafe { + graphics + .device + .create_pipeline_layout(&layout_create_info, None) + .unwrap() + }; + + let viewport = vk::Viewport::builder().width(1.0).height(1.0).max_depth(1.0).build(); + let scissor = vk::Rect2D::builder().extent(vk::Extent2D { width: 1, height: 1 }).build(); + + let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder() + .scissors(slice::from_ref(&scissor)) + .viewports(slice::from_ref(&viewport)) + .build(); + + let rasterization_info = vk::PipelineRasterizationStateCreateInfo::builder() + .front_face(vk::FrontFace::COUNTER_CLOCKWISE) + .line_width(1.0) + .polygon_mode(vk::PolygonMode::FILL) + .build(); + + let multisample_state_info = vk::PipelineMultisampleStateCreateInfo::builder() + .rasterization_samples(vk::SampleCountFlags::TYPE_1) + .build(); + + let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState { + blend_enable: 1, + src_color_blend_factor: vk::BlendFactor::SRC_COLOR, + dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_DST_COLOR, + color_blend_op: vk::BlendOp::ADD, + src_alpha_blend_factor: vk::BlendFactor::ZERO, + dst_alpha_blend_factor: vk::BlendFactor::ZERO, + alpha_blend_op: vk::BlendOp::ADD, + color_write_mask: vk::ColorComponentFlags::RGBA, + }]; + + let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder() + .logic_op(vk::LogicOp::CLEAR) + .attachments(&color_blend_attachment_states) + .build(); + + let dynamic_state = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; + let dynamic_state_info = + vk::PipelineDynamicStateCreateInfo::builder() + .dynamic_states(&dynamic_state) + .build(); + + let mut pipeline_rendering_create_info = vk::PipelineRenderingCreateInfo::builder() + .color_attachment_formats(slice::from_ref(&format)); + + let info = vk::GraphicsPipelineCreateInfo::builder() + .stages(&shader_stage_create_infos) + .vertex_input_state(&vertex_input_state_info) + .input_assembly_state(&vertex_input_assembly_state_info) + .viewport_state(&viewport_state_info) + .rasterization_state(&rasterization_info) + .multisample_state(&multisample_state_info) + .color_blend_state(&color_blend_state) + .dynamic_state(&dynamic_state_info) + .layout(pipeline_layout) + .push_next(&mut pipeline_rendering_create_info) + .build(); + + let pipeline = unsafe { graphics.device.create_graphics_pipelines(vk::PipelineCache::null(), slice::from_ref(&info), None).unwrap()[0] }; + + let descriptor_sizes = frag.descriptor_types.iter().flatten().map(|&desc_type| { + vk::DescriptorPoolSize { + ty: desc_type, + descriptor_count: 1, + } + }).collect::>(); + + let descriptor_pool_info = vk::DescriptorPoolCreateInfo::builder() + .pool_sizes(&descriptor_sizes) + .max_sets(1) + .build(); + + let descriptor_pool = unsafe { graphics.device.create_descriptor_pool(&descriptor_pool_info, None).unwrap() }; + + let desc_alloc_info = vk::DescriptorSetAllocateInfo::builder() + .descriptor_pool(descriptor_pool) + .set_layouts(&desc_set_layouts) + .build(); + + let descriptor_sets = unsafe { graphics.device.allocate_descriptor_sets(&desc_alloc_info).unwrap() }; + + let index_buffer = graphics.create_buffer(vk::BufferUsageFlags::INDEX_BUFFER, &INDICES); + let vertex_buffer = graphics.create_buffer(vk::BufferUsageFlags::VERTEX_BUFFER, &VERTICES); + + Self { + graphics, + pipeline, + pipeline_layout, + descriptor_pool, + desc_set_layouts, + descriptor_sets, + index_buffer, + vertex_buffer, + } + } + + pub fn render(&self, frame: &VkFrame, rect: vk::Rect2D) { + unsafe { + self.graphics.device.cmd_bind_pipeline(frame.command_buffer, vk::PipelineBindPoint::GRAPHICS, self.pipeline); + + let viewport = vk::Viewport::builder().width(rect.extent.width as _).height(rect.extent.height as _).max_depth(1.0).build(); + + self.graphics.device.cmd_set_viewport(frame.command_buffer, 0, slice::from_ref(&viewport)); + self.graphics.device.cmd_set_scissor(frame.command_buffer, 0, slice::from_ref(&rect)); + + self.graphics.device.cmd_bind_vertex_buffers(frame.command_buffer, 0, slice::from_ref(&self.vertex_buffer.buffer), &[0]); + self.graphics.device.cmd_bind_index_buffer(frame.command_buffer, self.index_buffer.buffer, 0, vk::IndexType::UINT16); + + self.graphics.device.cmd_draw_indexed(frame.command_buffer, 6, 1, 0, 0, 1); + } + } + + pub fn bind_descriptors(&self, descriptors: &[VkDescriptor]) { + let descriptor_write = descriptors.iter().enumerate().map(|(i, desciptor)| { + match desciptor { + VkDescriptor::Image(image) => { + let descriptor_image_info = vk::DescriptorImageInfo { + sampler: vk::Sampler::null(), + image_view: image.view.unwrap(), + image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }; + vk::WriteDescriptorSet::builder() + .dst_set(self.descriptor_sets[0]) + .dst_binding(i as u32) + .descriptor_type(vk::DescriptorType::SAMPLED_IMAGE) + .image_info(&[descriptor_image_info]) + .build() + }, + VkDescriptor::Buffer(buffer) => { + let descriptor_buffer_info = vk::DescriptorBufferInfo { + buffer: buffer.buffer, + offset: 0, + range: vk::WHOLE_SIZE, + }; + vk::WriteDescriptorSet::builder() + .dst_set(self.descriptor_sets[0]) + .dst_binding(i as u32) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) + .buffer_info(&[descriptor_buffer_info]) + .build() + }, + } + }).collect::>(); + println!("x"); + unsafe { self.graphics.device.update_descriptor_sets(&descriptor_write, &[]) }; + println!("y"); + } +} + +impl Drop for VkPipeline { + fn drop(&mut self) { + unsafe { + self.graphics.device.destroy_pipeline(self.pipeline, None); + self.graphics.device.destroy_pipeline_layout(self.pipeline_layout, None); + self.graphics.device.destroy_descriptor_pool(self.descriptor_pool, None); + for &desc_set_layout in self.desc_set_layouts.iter() { + self.graphics.device.destroy_descriptor_set_layout(desc_set_layout, None); + } + } + } +} + +pub enum VkDescriptor { + Image(Arc), + Buffer(Arc), +} + +pub fn find_memorytype_index( + memory_req: &vk::MemoryRequirements, + memory_prop: &vk::PhysicalDeviceMemoryProperties, + flags: vk::MemoryPropertyFlags, +) -> Option { + memory_prop.memory_types[..memory_prop.memory_type_count as _] + .iter() + .enumerate() + .find(|(index, memory_type)| { + (1 << index) & memory_req.memory_type_bits != 0 + && memory_type.property_flags & flags == flags + }) + .map(|(index, _memory_type)| index as _) +} + +/// Helper function for submitting command buffers. Immediately waits for the fence before the command buffer +/// is executed. That way we can delay the waiting for the fences by 1 frame which is good for performance. +/// Make sure to create the fence in a signaled state on the first use. +#[allow(clippy::too_many_arguments)] +pub fn record_submit_commandbuffer( + device: &ash::Device, + command_buffer: vk::CommandBuffer, + command_buffer_reuse_fence: vk::Fence, + submit_queue: vk::Queue, + wait_mask: &[vk::PipelineStageFlags], + wait_semaphores: &[vk::Semaphore], + signal_semaphores: &[vk::Semaphore], + f: F, +) { + unsafe { + device + .wait_for_fences(&[command_buffer_reuse_fence], true, std::u64::MAX) + .expect("Wait for fence failed."); + + device + .reset_fences(&[command_buffer_reuse_fence]) + .expect("Reset fences failed."); + + device + .reset_command_buffer( + command_buffer, + vk::CommandBufferResetFlags::RELEASE_RESOURCES, + ) + .expect("Reset command buffer failed."); + + let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + + device + .begin_command_buffer(command_buffer, &command_buffer_begin_info) + .expect("Begin commandbuffer"); + f(device, command_buffer); + device + .end_command_buffer(command_buffer) + .expect("End commandbuffer"); + + let command_buffers = vec![command_buffer]; + + let submit_info = vk::SubmitInfo::builder() + .wait_semaphores(wait_semaphores) + .wait_dst_stage_mask(wait_mask) + .command_buffers(&command_buffers) + .signal_semaphores(signal_semaphores); + + device + .queue_submit( + submit_queue, + &[submit_info.build()], + command_buffer_reuse_fence, + ) + .expect("queue submit failed."); + } +} + +unsafe extern "system" fn vulkan_debug_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, + _user_data: *mut std::os::raw::c_void, +) -> vk::Bool32 { + let callback_data = *p_callback_data; + let message_id_number = callback_data.message_id_number; + + let message_id_name = if callback_data.p_message_id_name.is_null() { + Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message_id_name).to_string_lossy() + }; + + let message = if callback_data.p_message.is_null() { + Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message).to_string_lossy() + }; + + println!( + "{message_severity:?}:\n{message_type:?} [{message_id_name} ({message_id_number})] : {message}\n", + ); + + vk::FALSE +} diff --git a/src/gui/font.rs b/src/gui/font.rs new file mode 100644 index 0000000..19641b5 --- /dev/null +++ b/src/gui/font.rs @@ -0,0 +1,211 @@ +use std::{rc::Rc, str::FromStr, sync::Arc}; + +use fontconfig::{FontConfig, OwnedPattern}; +use freetype::{bitmap::PixelMode, face::LoadFlag, Face, Library}; +use idmap::IdMap; +use log::debug; +use vulkano::{format::Format, command_buffer::CommandBufferUsage, image::ImmutableImage}; + +use crate::graphics::WlxGraphics; + +const PRIMARY_FONT: &str = "LiberationSans"; + +pub struct FontCache { + fc: FontConfig, + ft: Library, + collections: IdMap, +} + +struct FontCollection { + fonts: Vec, + cp_map: IdMap, +} + +struct Font { + face: Face, + path: String, + index: isize, + size: isize, + 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() -> Self { + let ft = Library::init().expect("Failed to initialize freetype"); + let fc = FontConfig::default(); + + FontCache { + fc, + ft, + collections: IdMap::new(), + } + } + + pub fn get_text_size(&mut self, text: &str, size: isize, graphics: Arc) -> (f32, f32) { + let sizef = size as f32; + + let height = sizef + ((text.lines().count() as f32) - 1f32) * (sizef * 1.5); + + let mut max_w = sizef * 0.33; + for line in text.lines() { + let w: f32 = line + .chars() + .map(|c| self.get_glyph_for_cp(c as usize, size, graphics.clone()).advance) + .sum(); + + if w > max_w { + max_w = w; + } + } + (max_w, height) + } + + pub fn get_glyphs(&mut self, text: &str, size: isize, graphics: Arc) -> Vec> { + let mut glyphs = Vec::new(); + for line in text.lines() { + for c in line.chars() { + glyphs.push(self.get_glyph_for_cp(c as usize, size, graphics.clone())); + } + } + 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(), + }, + ); + } + let coll = self.collections.get_mut(size).unwrap(); + + if let Some(font) = coll.cp_map.get(cp) { + return *font; + } + + let pattern_str = format!("{PRIMARY_FONT}-{size}:style=bold:charset={cp:04x}"); + + let mut pattern = + OwnedPattern::from_str(&pattern_str).expect("Failed to create fontconfig pattern"); + 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() { + debug!( + "Loading font: {} {}pt", + pattern.name().unwrap_or(path), + size + ); + + let font_idx = pattern.face_index().unwrap_or(0); + + let face = self + .ft + .new_face(path, font_idx as _) + .expect("Failed to load font face"); + face.set_char_size(size << 6, size << 6, 96, 96) + .expect("Failed to set font size"); + + let idx = coll.fonts.len(); + for cp in 0..0xFFFF { + if coll.cp_map.contains_key(cp) { + continue; + } + let g = face.get_char_index(cp); + if g > 0 { + coll.cp_map.insert(cp, idx); + } + } + + 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, + path: path.to_string(), + size, + index: font_idx as _, + glyphs, + }; + coll.fonts.push(font); + + idx + } else { + coll.cp_map.insert(cp, 0); + 0 + } + } + + fn get_glyph_for_cp(&mut self, cp: usize, size: isize, graphics: Arc) -> Rc { + let key = self.get_font_for_cp(cp, size); + + let font = &mut self.collections[size].fonts[key]; + + if let Some(glyph) = font.glyphs.get(cp) { + return glyph.clone(); + } + + if font.face.load_char(cp, LoadFlag::DEFAULT).is_err() { + return font.glyphs[0].clone(); + } + + let glyph = font.face.glyph(); + if glyph.render_glyph(freetype::RenderMode::Normal).is_err() { + return font.glyphs[0].clone(); + } + + let bmp = glyph.bitmap(); + let buf = bmp.buffer().to_vec(); + if buf.len() == 0 { + return font.glyphs[0].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 font.glyphs[0].clone(), + }; + + let mut cmd_buffer = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit); + let texture = cmd_buffer.texture2d(bmp.width() as _, bmp.rows() as _, format, buf); + let _ = cmd_buffer.end_and_execute(); + + 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)); + font.glyphs[cp].clone() + } +} diff --git a/src/gui/mod.rs b/src/gui/mod.rs new file mode 100644 index 0000000..cfb9734 --- /dev/null +++ b/src/gui/mod.rs @@ -0,0 +1,669 @@ +use std::sync::Arc; + +use glam::{Vec2, Vec3}; +use vulkano::{ + command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer}, + format::Format, + image::{view::ImageView, AttachmentImage}, + sampler::Filter, +}; + +use crate::{ + graphics::{WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline}, + overlays::{ + interactions::{InteractionHandler, PointerHit}, + OverlayBackend, OverlayRenderer, + }, + shaders::{frag_color, frag_glyph, frag_sprite, vert_common}, + state::AppState, +}; + +pub mod font; + +const RES_DIVIDER: usize = 4; + +struct Rect { + x: f32, + y: f32, + w: f32, + h: f32, +} + +// Parses a color from a HTML hex string +pub fn color_parse(html_hex: &str) -> Vec3 { + let mut color = Vec3::ZERO; + color.x = u8::from_str_radix(&html_hex[1..3], 16).unwrap() as f32 / 255.; + color.y = u8::from_str_radix(&html_hex[3..5], 16).unwrap() as f32 / 255.; + color.z = u8::from_str_radix(&html_hex[5..7], 16).unwrap() as f32 / 255.; + color +} + +pub struct CanvasBuilder { + canvas: Canvas, + + pub fg_color: Vec3, + pub bg_color: Vec3, + pub font_size: isize, +} + +impl CanvasBuilder { + pub fn new( + width: usize, + height: usize, + graphics: Arc, + format: Format, + data: D, + ) -> Self { + Self { + canvas: Canvas::new(width, height, graphics, format, data), + bg_color: Vec3::ZERO, + fg_color: Vec3::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) -> &mut Control { + let idx = self.canvas.controls.len(); + self.canvas.controls.push(Control { + rect: Rect { x, y, w, h }, + bg_color: self.bg_color, + on_render_bg: Some(Control::render_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, text: Arc) -> &mut Control { + let idx = self.canvas.controls.len(); + self.canvas.controls.push(Control { + rect: Rect { x, y, w, h }, + 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 + pub fn label_centered( + &mut self, + x: f32, + y: f32, + w: f32, + h: f32, + text: Arc, + ) -> &mut Control { + let idx = self.canvas.controls.len(); + self.canvas.controls.push(Control { + rect: Rect { x, y, w, h }, + 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 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, 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 }, + text, + fg_color: self.fg_color, + bg_color: self.bg_color, + size: self.font_size, + on_render_bg: Some(Control::render_rect), + on_render_fg: Some(Control::render_text_centered), + on_render_hl: Some(Control::render_highlight), + ..Control::new() + }); + + &mut self.canvas.controls[idx] + } + + pub fn key_button( + &mut self, + x: f32, + y: f32, + w: f32, + h: f32, + label: &Vec, + ) -> &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 }, + bg_color: self.bg_color, + on_render_bg: Some(Control::render_rect), + on_render_hl: Some(Control::render_highlight), + ..Control::new() + }); + + for (i, item) in label.iter().enumerate().take(label.len().min(2)) { + self.canvas.controls.push(Control { + rect: if i == 0 { + Rect { + x: x + 4., + y: y + (self.font_size as f32) + 4., + w, + h, + } + } else { + Rect { + x: x + w * 0.5, + y: y + h - (self.font_size as f32) + 4., + w, + h, + } + }, + text: Arc::from(item.as_str()), + fg_color: self.fg_color, + size: self.font_size, + on_render_fg: Some(Control::render_text), + ..Control::new() + }); + } + + &mut self.canvas.controls[idx] + } +} + +pub struct CanvasData { + pub data: D, + pub width: usize, + pub height: usize, + + graphics: Arc, + + pipeline_color: Arc, + pipeline_glyph: Arc, +} + +pub struct Canvas { + controls: Vec>, + canvas: CanvasData, + + hover_controls: [Option; 2], + pressed_controls: [Option; 2], + + interact_map: Vec>, + interact_stride: usize, + interact_rows: usize, + + tex_fg: Arc, + view_fg: Arc>, + tex_bg: Arc, + view_bg: Arc>, + tex_final: Arc, + view_final: Arc>, + + pass_fg: WlxPass, + pass_bg: WlxPass, +} + +impl Canvas { + fn new( + width: usize, + height: usize, + graphics: Arc, + format: Format, + data: D, + ) -> Self { + let pipeline_color = graphics.create_pipeline( + vert_common::load(graphics.device.clone()).unwrap(), + frag_color::load(graphics.device.clone()).unwrap(), + format, + ); + + let pipeline_glyph = graphics.create_pipeline( + vert_common::load(graphics.device.clone()).unwrap(), + frag_glyph::load(graphics.device.clone()).unwrap(), + format, + ); + + let vertex_buffer = + graphics.upload_verts(width as _, height as _, 0., 0., width as _, height as _); + + let pipeline = graphics.create_pipeline( + vert_common::load(graphics.device.clone()).unwrap(), + frag_sprite::load(graphics.device.clone()).unwrap(), + format, + ); + + let tex_fg = graphics.render_texture(width as _, height as _, format); + let tex_bg = graphics.render_texture(width as _, height as _, format); + let tex_final = graphics.render_texture(width as _, height as _, format); + + let view_fg = ImageView::new_default(tex_fg.clone()).unwrap(); + let view_bg = ImageView::new_default(tex_bg.clone()).unwrap(); + let view_final = ImageView::new_default(tex_final.clone()).unwrap(); + + let set_fg = pipeline.uniform_sampler(0, view_fg.clone(), Filter::Nearest); + let set_bg = pipeline.uniform_sampler(0, view_bg.clone(), Filter::Nearest); + let pass_fg = pipeline.create_pass( + [width as _, height as _], + vertex_buffer.clone(), + graphics.quad_indices.clone(), + vec![set_fg], + ); + let pass_bg = pipeline.create_pass( + [width as _, height as _], + vertex_buffer.clone(), + graphics.quad_indices.clone(), + vec![set_bg], + ); + + let stride = width / RES_DIVIDER; + let rows = height / RES_DIVIDER; + + Self { + canvas: CanvasData { + data, + width, + height, + graphics, + pipeline_color, + pipeline_glyph, + }, + controls: Vec::new(), + hover_controls: [None, None], + pressed_controls: [None, None], + interact_map: vec![None; stride * rows], + interact_stride: stride, + interact_rows: rows, + tex_fg, + view_fg, + tex_bg, + view_bg, + tex_final, + view_final, + pass_fg, + pass_bg, + } + } + + 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 u8); + } + } + } + + fn interactive_get_idx(&self, uv: Vec2) -> Option { + let x = (uv.x * self.canvas.width as f32) as usize; + let y = (uv.y * self.canvas.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) + } + + fn render_bg(&mut self, app: &mut AppState) { + let mut cmd_buffer = self + .canvas + .graphics + .create_command_buffer(CommandBufferUsage::OneTimeSubmit) + .begin(self.view_bg.clone()); + for c in self.controls.iter_mut() { + if let Some(fun) = c.on_render_bg { + fun(c, &self.canvas, app, &mut cmd_buffer); + } + } + let _ = cmd_buffer.end_render_and_execute(); + } + + fn render_fg(&mut self, app: &mut AppState) { + let mut cmd_buffer = self + .canvas + .graphics + .create_command_buffer(CommandBufferUsage::OneTimeSubmit) + .begin(self.view_fg.clone()); + for c in self.controls.iter_mut() { + if let Some(fun) = c.on_render_fg { + fun(c, &self.canvas, app, &mut cmd_buffer); + } + } + let _ = cmd_buffer.end_render_and_execute(); + } + + pub fn render_view(&self) -> Arc> { + self.view_final.clone() + } +} + +impl InteractionHandler for Canvas { + fn on_left(&mut self, _app: &mut AppState, hand: usize) { + self.hover_controls[hand] = None; + } + fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) { + if let Some(i) = self.interactive_get_idx(hit.uv) { + self.hover_controls[hit.hand] = Some(i); + } else { + self.hover_controls[hit.hand] = None; + } + } + 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.hand] + }; + + 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.hand] = Some(idx); + f(c, &mut self.canvas.data, app); + } + } else if let Some(ref mut f) = c.on_release { + self.pressed_controls[hit.hand] = None; + f(c, &mut self.canvas.data, app); + } + } + } + fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {} +} + +impl OverlayRenderer for Canvas { + fn init(&mut self, app: &mut AppState) { + self.render_bg(app); + + self.render_fg(app); + } + fn pause(&mut self, _app: &mut AppState) {} + fn resume(&mut self, _app: &mut AppState) {} + fn render(&mut self, app: &mut AppState) { + let mut dirty = false; + + for c in self.controls.iter_mut() { + if let Some(fun) = c.on_update { + fun(c, &mut self.canvas.data, app); + } + if c.dirty { + dirty = true; + c.dirty = false; + } + } + + let mut cmd_buffer = self + .canvas + .graphics + .create_command_buffer(CommandBufferUsage::OneTimeSubmit) + .begin(self.view_final.clone()); + + if dirty { + self.render_fg(app); + } + + // static background + cmd_buffer.run_ref(&self.pass_bg); + + 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 test(c, &mut self.canvas.data, app) { + render(c, &self.canvas, app, &mut cmd_buffer, true); + } + } + if self.hover_controls.contains(&Some(i)) { + render(c, &self.canvas, app, &mut cmd_buffer, false); + } + } + } + + // mostly static text + cmd_buffer.run_ref(&self.pass_fg); + + let _ = cmd_buffer.end_render_and_execute(); + } + fn view(&mut self) -> Arc { + self.view_final.clone() + } +} + +impl OverlayBackend for Canvas {} + +pub struct Control { + pub state: Option, + rect: Rect, + fg_color: Vec3, + bg_color: Vec3, + text: Arc, + size: isize, + dirty: bool, + pass_hl: Option<(WlxPass, WlxPass)>, + + pub on_update: Option, + pub on_press: Option, + pub on_release: Option, + pub test_highlight: Option bool>, + + on_render_bg: Option< + fn(&Self, &CanvasData, &mut AppState, &mut WlxCommandBuffer), + >, + on_render_hl: Option< + fn( + &Self, + &CanvasData, + &mut AppState, + &mut WlxCommandBuffer, + bool, + ), + >, + on_render_fg: Option< + fn(&Self, &CanvasData, &mut AppState, &mut WlxCommandBuffer), + >, +} + +impl Control { + fn new() -> Self { + Self { + rect: Rect { + x: 0., + y: 0., + w: 0., + h: 0., + }, + fg_color: Vec3::ONE, + bg_color: Vec3::ZERO, + text: Arc::from(""), + dirty: false, + 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, + pass_hl: None, + } + } + + #[inline(always)] + pub fn set_text(&mut self, text: &str) { + if *self.text == *text { + return; + } + self.text = text.into(); + self.dirty = true; + } + + #[inline(always)] + pub fn get_text(&self) -> &str { + &self.text + } + + fn render_rect( + &self, + canvas: &CanvasData, + _: &mut AppState, + cmd_buffer: &mut WlxCommandBuffer, + ) { + 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 set0 = canvas.pipeline_color.uniform_buffer( + 0, + vec![self.bg_color.x, self.bg_color.y, self.bg_color.z, 1.], + ); + canvas.pipeline_color.create_pass( + [canvas.width as _, canvas.height as _], + vertex_buffer, + canvas.graphics.quad_indices.clone(), + vec![set0], + ) + }; + + cmd_buffer.run_ref(&pass); + } + + fn render_highlight( + &self, + canvas: &CanvasData, + _: &mut AppState, + cmd_buffer: &mut WlxCommandBuffer, + strong: bool, + ) { + 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_color.uniform_buffer( + 0, + vec![ + self.bg_color.x, + self.bg_color.y, + self.bg_color.z, + if strong { 0.5 } else { 0.3 }, + ], + ); + let pass = canvas.pipeline_color.create_pass( + [canvas.width as _, canvas.height as _], + vertex_buffer.clone(), + canvas.graphics.quad_indices.clone(), + vec![set0], + ); + + cmd_buffer.run_ref(&pass); + } + + fn render_text( + &self, + canvas: &CanvasData, + app: &mut AppState, + cmd_buffer: &mut WlxCommandBuffer, + ) { + 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_glyph.uniform_sampler( + 0, + ImageView::new_default(tex).unwrap(), + Filter::Nearest, + ); + let set1 = canvas.pipeline_glyph.uniform_buffer( + 1, + vec![self.fg_color.x, self.fg_color.y, self.fg_color.z, 1.], + ); + let pass = canvas.pipeline_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; + } + } + fn render_text_centered( + &self, + canvas: &CanvasData, + app: &mut AppState, + cmd_buffer: &mut WlxCommandBuffer, + ) { + let (w, h) = app + .fc + .get_text_size(&self.text, self.size, canvas.graphics.clone()); + + let mut cur_y = self.rect.y + (self.rect.h) - (h * 0.5); + for line in self.text.lines() { + let mut cur_x = self.rect.x + (self.rect.w * 0.5) - (w * 0.5); + 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_glyph.uniform_sampler( + 0, + ImageView::new_default(tex).unwrap(), + Filter::Nearest, + ); + let set1 = canvas.pipeline_glyph.uniform_buffer( + 1, + vec![self.fg_color.x, self.fg_color.y, self.fg_color.z, 1.], + ); + let pass = canvas.pipeline_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; + } + } +} diff --git a/src/image.png b/src/image.png new file mode 100644 index 0000000000000000000000000000000000000000..b9e2802558552cf9eee7dcea2a5df97c3b2e6ff5 GIT binary patch literal 148030 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

bPBT}p%>Zi?LLo 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

lz@mzcW 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 literal 0 HcmV?d00001 diff --git a/src/res/actions.json b/src/res/actions.json new file mode 100644 index 0000000..0411b91 --- /dev/null +++ b/src/res/actions.json @@ -0,0 +1,83 @@ +{ + "actions": [ + { + "name": "/actions/default/in/Click", + "type": "boolean", + "requirement": "mandatory" + }, + { + "name": "/actions/default/in/Grab", + "type": "boolean", + "requirement": "mandatory" + }, + { + "name": "/actions/default/in/Scroll", + "type": "vector2", + "requirement": "mandatory" + }, + { + "name": "/actions/default/in/ShowHide", + "type": "boolean", + "requirement": "mandatory" + }, + { + "name": "/actions/default/in/AltClick", + "type": "boolean", + "requirement": "optional" + }, + { + "name": "/actions/default/in/ClickModifierRight", + "type": "boolean", + "requirement": "optional" + }, + { + "name": "/actions/default/in/ClickModifierMiddle", + "type": "boolean", + "requirement": "optional" + }, + { + "name": "/actions/default/in/SpaceDrag", + "type": "boolean", + "requirement": "optional" + }, + { + "name": "/actions/default/in/LeftHand", + "type": "pose", + "requirement": "optional" + }, + { + "name": "/actions/default/in/RightHand", + "type": "pose", + "requirement": "optional" + }, + { + "name": "/actions/default/out/HapticsLeft", + "type": "vibration" + }, + { + "name": "/actions/default/out/HapticsRight", + "type": "vibration" + } + ], + "action_sets": [ + { + "name": "/actions/default", + "usage": "leftright" + } + ], + "default_bindings": [ + { + "controller_type": "knuckles", + "binding_url": "actions_binding_knuckles.json" + }, + { + "controller_type": "oculus_touch", + "binding_url": "actions_binding_oculus.json" + }, + { + "controller_type": "vive_controller", + "binding_url": "actions_binding_vive.json" + } + ], + "localization": [] +} diff --git a/src/res/actions_binding_knuckles.json b/src/res/actions_binding_knuckles.json new file mode 100644 index 0000000..1f19b08 --- /dev/null +++ b/src/res/actions_binding_knuckles.json @@ -0,0 +1,173 @@ +{ + "action_manifest_version" : 0, + "app_key" : "galister.wlxoverlay", + "bindings" : { + "/actions/default" : { + "haptics" : [ + { + "output" : "/actions/default/out/hapticsleft", + "path" : "/user/hand/left/output/haptic" + }, + { + "output" : "/actions/default/out/hapticsright", + "path" : "/user/hand/right/output/haptic" + } + ], + "poses" : [ + { + "output" : "/actions/default/in/lefthand", + "path" : "/user/hand/left/pose/tip" + }, + { + "output" : "/actions/default/in/righthand", + "path" : "/user/hand/right/pose/tip" + } + ], + "sources" : [ + { + "inputs" : { + "double" : { + "output" : "/actions/default/in/showhide" + }, + "touch" : { + "output": "/actions/default/in/clickmodifierright" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/b" + }, + { + "path": "/user/hand/left/input/a", + "mode": "button", + "inputs": { + "touch": { + "output": "/actions/default/in/clickmodifiermiddle" + } + } + }, + { + "path": "/user/hand/right/input/b", + "mode": "button", + "inputs": { + "touch": { + "output": "/actions/default/in/clickmodifierright" + } + } + }, + { + "path": "/user/hand/right/input/a", + "mode": "button", + "inputs": { + "touch": { + "output": "/actions/default/in/clickmodifiermiddle" + } + } + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/click" + } + }, + "mode" : "button", + "parameters" : { + "click_activate_threshold" : "0.35", + "click_deactivate_threshold" : "0.31" + }, + "path" : "/user/hand/left/input/trigger" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/click" + } + }, + "mode" : "button", + "parameters" : { + "click_activate_threshold" : "0.35", + "click_deactivate_threshold" : "0.31" + }, + "path" : "/user/hand/right/input/trigger" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/altclick" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/trackpad" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/altclick" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/trackpad" + }, + { + "inputs" : { + "grab" : { + "output" : "/actions/default/in/grab" + } + }, + "mode" : "grab", + "parameters" : { + "value_hold_threshold" : "1.3", + "value_release_threshold" : "1.1" + }, + "path" : "/user/hand/left/input/grip" + }, + { + "inputs" : { + "grab" : { + "output" : "/actions/default/in/grab" + } + }, + "mode" : "grab", + "parameters" : { + "value_hold_threshold" : "1.3", + "value_release_threshold" : "1.1" + }, + "path" : "/user/hand/right/input/grip" + }, + { + "inputs" : { + "scroll" : { + "output" : "/actions/default/in/scroll" + } + }, + "mode" : "scroll", + "parameters" : { + "scroll_mode" : "smooth" + }, + "path" : "/user/hand/left/input/thumbstick" + }, + { + "inputs" : { + "scroll" : { + "output" : "/actions/default/in/scroll" + } + }, + "mode" : "scroll", + "parameters" : { + "scroll_mode" : "smooth" + }, + "path" : "/user/hand/right/input/thumbstick" + } + ] + } + }, + "category" : "steamvr_input", + "controller_type" : "knuckles", + "description" : "Ver1", + "interaction_profile" : "", + "name" : "WlxOverlay configuration for Index Controller", + "options" : { + "mirror_actions" : false, + "simulated_controller_type" : "none" + }, + "simulated_actions" : [] +} diff --git a/src/res/actions_binding_oculus.json b/src/res/actions_binding_oculus.json new file mode 100644 index 0000000..37c8268 --- /dev/null +++ b/src/res/actions_binding_oculus.json @@ -0,0 +1,139 @@ +{ + "action_manifest_version" : 0, + "app_key" : "galister.wlxoverlay", + "bindings" : { + "/actions/default" : { + "haptics" : [ + { + "output" : "/actions/default/out/hapticsleft", + "path" : "/user/hand/left/output/haptic" + }, + { + "output" : "/actions/default/out/hapticsright", + "path" : "/user/hand/right/output/haptic" + } + ], + "poses" : [ + { + "output" : "/actions/default/in/lefthand", + "path" : "/user/hand/left/pose/tip" + }, + { + "output" : "/actions/default/in/righthand", + "path" : "/user/hand/right/pose/tip" + } + ], + "sources" : [ + { + "inputs" : { + "double" : { + "output" : "/actions/default/in/showhide" + }, + "touch" : { + "output": "/actions/default/in/clickmodifierright" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/y" + }, + { + "path": "/user/hand/left/input/x", + "mode": "button", + "inputs": { + "touch": { + "output": "/actions/default/in/clickmodifiermiddle" + } + } + }, + { + "path": "/user/hand/right/input/b", + "mode": "button", + "inputs": { + "touch": { + "output": "/actions/default/in/clickmodifierright" + } + } + }, + { + "path": "/user/hand/right/input/a", + "mode": "button", + "inputs": { + "touch": { + "output": "/actions/default/in/clickmodifiermiddle" + } + } + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/click" + } + }, + "mode": "button", + "path" : "/user/hand/left/input/trigger" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/click" + } + }, + "mode": "button", + "path" : "/user/hand/right/input/trigger" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/grab" + } + }, + "mode": "button", + "path" : "/user/hand/left/input/grip" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/default/in/grab" + } + }, + "mode": "button", + "path" : "/user/hand/right/input/grip" + }, + { + "inputs" : { + "scroll" : { + "output" : "/actions/default/in/scroll" + } + }, + "mode" : "scroll", + "parameters" : { + "scroll_mode" : "smooth" + }, + "path" : "/user/hand/left/input/joystick" + }, + { + "inputs" : { + "scroll" : { + "output" : "/actions/default/in/scroll" + } + }, + "mode" : "scroll", + "parameters" : { + "scroll_mode" : "smooth" + }, + "path" : "/user/hand/right/input/joystick" + } + ] + } + }, + "category" : "steamvr_input", + "controller_type" : "oculus_touch", + "description" : "Ver1", + "interaction_profile" : "", + "name" : "WlxOverlay configuration for Oculus Touch Controller", + "options" : { + "mirror_actions" : false, + "simulated_controller_type" : "none" + }, + "simulated_actions" : [] +} diff --git a/src/res/actions_binding_vive.json b/src/res/actions_binding_vive.json new file mode 100644 index 0000000..a3db7d8 --- /dev/null +++ b/src/res/actions_binding_vive.json @@ -0,0 +1,126 @@ +{ + "action_manifest_version" : 0, + "app_key" : "galister.wlxoverlay", + "bindings" : { + "/actions/default": { + "haptics" : [ + { + "output" : "/actions/default/out/hapticsleft", + "path" : "/user/hand/left/output/haptic" + }, + { + "output" : "/actions/default/out/hapticsright", + "path" : "/user/hand/right/output/haptic" + } + ], + "sources": [ + { + "path": "/user/hand/left/input/grip", + "mode": "button", + "inputs": { + "click": { + "output": "/actions/default/in/grab" + } + } + }, + { + "path": "/user/hand/right/input/grip", + "mode": "button", + "inputs": { + "click": { + "output": "/actions/default/in/grab" + } + } + }, + { + "path": "/user/hand/left/input/trigger", + "mode": "button", + "inputs": { + "click": { + "output": "/actions/default/in/click" + } + } + }, + { + "path": "/user/hand/right/input/trigger", + "mode": "trigger", + "inputs": { + "click": { + "output": "/actions/default/in/click" + } + } + }, + { + "path": "/user/hand/right/input/trackpad", + "mode": "scroll", + "parameters": { + "scroll_mode": "smooth" + }, + "inputs": { + "scroll": { + "output": "/actions/default/in/scroll" + } + } + }, + { + "path": "/user/hand/left/input/trackpad", + "mode": "scroll", + "inputs": { + "scroll": { + "output": "/actions/default/in/scroll" + } + } + }, + { + "path": "/user/hand/left/input/application_menu", + "mode": "button", + "inputs": { + "double": { + "output": "/actions/default/in/showhide" + } + } + }, + { + "path": "/user/hand/right/input/trackpad", + "mode": "dpad", + "parameters": { + "sub_mode": "touch" + }, + "inputs": { + "west": { + "output": "/actions/default/in/clickmodifiermiddle" + }, + "east": { + "output": "/actions/default/in/clickmodifierright" + } + } + }, + { + "path": "/user/hand/left/input/trackpad", + "mode": "dpad", + "parameters": { + "sub_mode": "touch" + }, + "inputs": { + "west": { + "output": "/actions/default/in/clickmodifiermiddle" + }, + "east": { + "output": "/actions/default/in/clickmodifierright" + } + } + } + ] + } + }, + "category" : "steamvr_input", + "controller_type" : "vive_controller", + "description" : "Ver1", + "interaction_profile" : "", + "name" : "WlxOverlay configuration for Vive Controller", + "options" : { + "mirror_actions" : false, + "simulated_controller_type" : "none" + }, + "simulated_actions" : [] +} diff --git a/src/res/keyboard.yaml b/src/res/keyboard.yaml new file mode 100644 index 0000000..ac760d2 --- /dev/null +++ b/src/res/keyboard.yaml @@ -0,0 +1,127 @@ +--- + +# This file contains all data needed to generate the keyboard. +# You can create any layout, as long as: +# - All keys are rectangular with 1 unit of height. +# This means: +# - We're limited to the flat & boring ANSI enter key. +# - Numpad + and Enter might not look so great. + +# *** Important *** +# The keyboard layout uses virtual key codes, so they are layout-independent. +# For example, Q on a French layout actually results in A. +# If you're using a non-english layout, chances are you only need to edit the label section below. + +# Not used for anything right now +name: "en-us_full" + +# How many units of key size in each row? 1 = standard letter key size +row_size: 23 + +# Specifies the size of each key. The sum of any given row must equal RowSize +key_sizes: + - [1.5,0.5, 1, 1, 1, 1,0.5,1, 1, 1, 1,0.5,1, 1, 1, 1, 0.5, 1, 1, 1, 4.5] + - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1] + - [1.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.5, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1] + - [1.75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.25, 4, 1, 1, 1, 1] + - [2.25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.75, 1.5, 1, 1.5, 1, 1, 1, 1] + - [1.25, 1.25, 1.25, 6.25, 1.25, 1.25, 1.25, 1.25, 0.5, 1, 1, 1, 0.5, 2, 1, 1] + +# The main (blue) layout of the keyboard. +# Accepted are: +# - virtual keys, for a full list go to https://github.com/galister/X11Overlay/blob/master/Types/VirtualKey.cs +# - ExecCommands (defined below) +# - Macros (defined below) +# - ~ (null) will leave an empty space with the corresponding size from key_sizes +main_layout: + - ["Escape", ~, "F1", "F2", "F3", "F4", ~, "F5", "F6", "F7", "F8", ~, "F9", "F10", "F11", "F12", ~, "Print", "Scroll", "Pause", ~] + - ["Oem3", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", "N0", "Minus", "Plus", "BackSpace", ~, "Insert", "Home", "Prior", ~, "NumLock", "KP_Divide", "KP_Multiply", "KP_Subtract"] + - ["Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Oem4", "Oem6", "Oem5", ~, "Delete", "End", "Next", ~, "KP_7", "KP_8", "KP_9", "KP_Add"] + - ["XF86Favorites", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Oem1", "Oem7", "Return", ~, "KP_4", "KP_5", "KP_6", ~] + - ["LShift", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Period", "Oem2", "RShift", ~, "Up", ~, "KP_1", "KP_2", "KP_3", "KP_Enter"] + - ["LCtrl", "LSuper", "LAlt", "Space", "Meta", "RSuper", "Menu", "RCtrl", ~, "Left", "Down", "Right", ~, "KP_0", "KP_Decimal", ~] + +# When using the purple pointer... +# None - No special functionality when using purple pointer +# Shift - Use same functionality as the orange pointer +# Ctrl - Use Main layout with Ctrl modifier +# Super - Use Main layout with Super (WinKey) modifier +# Meta - Use Main layout with Meta (AltGr) modifier +# Layout - Use the alt_layout defined below +alt_layout_mode: Ctrl + +# The alt (fn) layout of the keyboard. Only takes effect when alt_layout_mode: Layout +# Accepted values are the same as for MainLayout. +# The default layout here is a dummy. Change it to fit your liking. +alt_layout: + - ["Escape", ~, "F1", "F2", "F3", "F4", ~, "F5", "F6", "F7", "F8", ~, "F9", "F10", "F11", "F12", ~, "Print", "Scroll", "Pause", ~, "COPY", "PASTE"] + - ["Oem3", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Minus", "Plus", "BackSpace", ~, "Insert", "Home", "Prior", ~, "NumLock", "KP_Divide", "KP_Multiply", "KP_Subtract"] + - ["Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Oem4", "Oem6", "Oem5", ~, "Delete", "End", "Next", ~, "KP_7", "KP_8", "KP_9", "KP_Add"] + - ["XF86Favorites", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Oem1", "Oem7", "Return", ~, "KP_4", "KP_5", "KP_6", ~] + - ["LShift", "Oem102", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Period", "Oem2", "RShift", ~, "Up", ~, "KP_1", "KP_2", "KP_3", "KP_Enter"] + - ["LCtrl", "LSuper", "LAlt", "Space", "Meta", "RSuper", "Menu", "RCtrl", ~, "Left", "Down", "Right", ~, "KP_0", "KP_Decimal", ~] + +# Shell commands to be used in a layout. +# Value is an array of string arguments. +exec_commands: + STT: [ "whisper_stt", "--lang", "en" ] + +# Series of keypresses to be used in a layout. +# Format: keyName [DOWN|UP] +# keyName must be a valid key from `xmodmap -pke` +# DOWN|UP: can be omitted for an implicit "keyName DOWN, keyName UP" +macros: + KILL: [ "LSuper DOWN", "LCtrl DOWN", "Escape", "LCtrl UP", "LSuper UP" ] + COPY: [ "LCtrl DOWN", "C", "LCtrl UP" ] + PASTE: [ "LCtrl DOWN", "V", "LCtrl UP" ] + +# Custom labels to use. +# Key: element of MainLayout / AltLayout +# Value: Array of strings. 0th element is the upper row, 1st element is lower row. +# For empty labels, use [] (do not use ~) +labels: + "N1": ["1", "!"] + "N2": ["2", "@"] + "N3": ["3", "#"] + "N4": ["4", "$"] + "N5": ["5", "%"] + "N6": ["6", "^"] + "N7": ["7", "&"] + "N8": ["8", "*"] + "N9": ["9", "("] + "N0": ["0", ")"] + "Escape": ["Esc"] + "Prior": ["PgUp"] + "Next": ["PgDn"] + "NumLock": ["Num"] + "Space": [] + "LAlt": ["Alt"] + "LCtrl": ["Ctrl"] + "RCtrl": ["Ctrl"] + "LSuper": ["Super"] + "RSuper": ["Super"] + "LShift": ["Shift"] + "RShift": ["Shift"] + "Insert": ["Ins"] + "Delete": ["Del"] + "Minus": ["-", "_"] + "Plus": ["=", "+"] + "BackSpace": ["<<"] + "Comma": [" ,", "<"] + "Period": [" .", ">"] + "Oem1": [" ;", ":"] + "Oem2": [" /", "?"] + "Oem3": ["`", "~"] + "Oem4": [" [", "{"] + "Oem5": [" \\", "|"] + "Oem6": [" ]", "}"] + "Oem7": [" '", "\""] + "Oem102": [" \\", "|"] + "KP_Divide": [" /"] + "KP_Add": [" +"] + "KP_Multiply": [" *"] + "KP_Decimal": [" ."] + "KP_Subtract": [" -"] + "KP_Enter": ["Ent"] + "XF86Favorites": ["Rofi"] + diff --git a/src/shaders/frag-color.spv b/src/shaders/frag-color.spv new file mode 100644 index 0000000000000000000000000000000000000000..4cab844fe8804a839ecc6fe6d2398874294a4c79 GIT binary patch literal 696 zcmYk3OH0F05QV3Yw6(P_YjsyzyA+BGErKYhNL&QE@CO7*vk1nd^acKNH-hK8t9j5^IY$Ivks#?&6ny6R#$u`e@gS{!?ab!VS+8m{;6V?>=;mnHaqBjvsLt! zv)4e5z3z3nk2z$%>giQ<_p;Nv;6X+`+9Q|ka`gZ7da9ys*s3vX~6is*yEC1)P6zVfExP+pK%J81-#MxacvIGc(t7uh%?TGHckXt=k(btY=LzCa&XRF#0hX&hqi_{fBobHq3KE zG+kS=mLmS^Cdt@%Du3ak=XfM9t038UNFaF+i`<1kIKI27XgR=Th1Uae1Rjpw+gFm-*!#WR38Lqp!z9itnlAGgVRORJ8e@4Tg(HRo_U z_C!BsQRFMsMm=m#`74U(eDz<2l);^qxSG+n@P>Hm(HnhNky`NR!OR8@{WC@M-1(;d zpDX%u>}`oR3+U=?Yt6TjM?En24R@%2=Eokrj}$ov99pn1hoje^#0^Dyfsf@F`P>0o z>YeItSaVK{eh*^WiqvMVyi?-2F!SL(FdH!6-;?w0*UF+MZvqYccX7;tw^6;73w5C; z95vYAk;7GZ_YvNEg!fDQO(&!0ft(uTp=JM4z4_Lca$n92sE_`Y9R9vM^J}fC0nhoE LJ=|Yw*hl$4#*0Xk literal 0 HcmV?d00001 diff --git a/src/shaders/frag-sprite.spv b/src/shaders/frag-sprite.spv new file mode 100644 index 0000000000000000000000000000000000000000..662225b06e1a13e795485007ef2278baf9e0c739 GIT binary patch literal 764 zcmY+B&r1Sv5XC2dq-myRY7&IjF^~==K~!{zy)1a>&~XXf!@{SIv%3Vn&#JY3 zVRq)ddEc4&&gOIHC9@r?TFrX4K26JsF>$-8cid-pxQwU6tLsY?2bNETXzEt7f;{&2 z8bl=aWKCH^I(Riuluv7obg5t!b6n3Ej~(~Po6P6lGX4mCFAO5zn+6Mi5(g_ET{)u* zqDh!cvx1@&*}g?9roCrL>`i9jY@xnzHTfX&V*fo(7OKlV<`b8!7NJ*M+Iaoah_!)PhG3=6!G%!f20VW}gpj z;kKeaP=`FYBV%6bbvH5hM*0i%;jJ7rUt)^#cVd~5y)qw|84hLif02rvuv;|n_o~2d W;2xEy*Rc$a8OVc~_ggQxk^KOq=r-5@ literal 0 HcmV?d00001 diff --git a/src/shaders/frag-srgb.spv b/src/shaders/frag-srgb.spv new file mode 100644 index 0000000000000000000000000000000000000000..513121a98823c717e242c96236b7159279e26b7a GIT binary patch literal 1316 zcmY+E%T5$Q6o$*W01hA^2#$D}5%C6&nkX?b<7HubGl4`GE<2>BkxtraGIS#>UFj?6 z8@OT36Zs5mNc_K=E+{)WQ|JHBU*}YvQ<-XIW!#ucX3|WX=VtWHnTi-AZp^Mh?^Ca{ zpZ7a2UOq!{#Z;{j&5Rj0HLcj!?PNgUvTRN^D;>NF$gA318`@Me4HNXjU~4PreF&qy zy>LH2O5-q1265O=hH;c92Qj({PnQg$wCMYSx)grhx~bdRS&@fPmS#iMg`3uz48lA< z%!{G&vXAqLD-NutW|p)@MV{^KjN-S1catx>v67&FD4b^BfF=;;j<;oef$l~PK4SQH zExN$%b6i#bMju+v;pdqzWAmCpMSk0M&RBm@{+PIyY)O0RE0`X%^gE%I$-uuZG3SJ9 z>ckDL=rw}>K8Kg3oKyrI`KC%09QnZRp46WaCLiZSKdY5o@aVzZ4{kxK1+DD6X7RtAY|8!~uXfiJA$FtW;GI$PrZ92L?Y$%aIM_bl+Y*NJ&1soaxAEhxtFs~u z=bO{6ivRtq`OazY3&Z*5w2#GqJlTBjv}?j}zHR7b(L3hH$DB^Z)V0z-`og>9ZeZ@p zyYw;d65LgsIq|lb5tun#l`(_Q%8HKng$DkOD)K(y&=bEdgFBI)c`TP4{YTGv;GCYf zi~f6=zbzlGCHq-&_vFLV+bLC{&I1|mg1p4(CHc7@F_-_5eDb?o>+<2~CwlG==k!nH jlf&VTd^BM0^Hjzi{^*r)4*t;K(6QI`1pZ7Xd?ot_)t^^i literal 0 HcmV?d00001 diff --git a/src/shaders/mod.rs b/src/shaders/mod.rs new file mode 100644 index 0000000..15e979a --- /dev/null +++ b/src/shaders/mod.rs @@ -0,0 +1,108 @@ +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; + }; + + void main() + { + out_color = in_color; + } + ", + } +} + +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_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() + { + vec4 c = texture(in_texture, in_uv); + out_color.rgb = c.rgb; + out_color.a = min((c.r + c.g + c.b)*100.0, 1.0); + } + ", + } +} + +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; + + 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); + } + ", + } +} diff --git a/src/shaders/src/color.frag b/src/shaders/src/color.frag new file mode 100644 index 0000000..e776b5a --- /dev/null +++ b/src/shaders/src/color.frag @@ -0,0 +1,15 @@ +#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; +}; + +void main() +{ + out_color = in_color; +} + diff --git a/src/shaders/src/common.vert b/src/shaders/src/common.vert new file mode 100644 index 0000000..1cd48e8 --- /dev/null +++ b/src/shaders/src/common.vert @@ -0,0 +1,12 @@ +#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.); +} + diff --git a/src/shaders/src/glyph.frag b/src/shaders/src/glyph.frag new file mode 100644 index 0000000..397bf68 --- /dev/null +++ b/src/shaders/src/glyph.frag @@ -0,0 +1,18 @@ +#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 = 0, binding = 1) 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; +} + diff --git a/src/shaders/src/sprite.frag b/src/shaders/src/sprite.frag new file mode 100644 index 0000000..d3245f0 --- /dev/null +++ b/src/shaders/src/sprite.frag @@ -0,0 +1,14 @@ +#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); + out_color.a = 1.; +} + diff --git a/src/shaders/src/srgb.frag b/src/shaders/src/srgb.frag new file mode 100644 index 0000000..388593d --- /dev/null +++ b/src/shaders/src/srgb.frag @@ -0,0 +1,19 @@ +#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); + + 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); +} + diff --git a/src/shaders/vert-common.spv b/src/shaders/vert-common.spv new file mode 100644 index 0000000000000000000000000000000000000000..8107a011775cb2143f6b58f08acd11a65f39ad38 GIT binary patch literal 964 zcmYk4OG_(35QWPm#<%hPeneaf;zC6b@4YT$Tm*~=ZU;#QX^4}MOhjD??)|-YBY3`^ zX-J2fu5;>4)#;v;ONSF?V>V-RR=0dDT1kwFtNJ}^JT#66S^N0(>;%QUmAw#6&8B=_ z(evfWr2v>2#e!m4QPvObxgHgpwx|(DcXv_aCT{h5@gRHeByq>3N!)h*q?NhX1l{C_ z&ZVu+ustdmmtu4#n7Q3y77t&|z<6+}hr(6)Hhb>G&7}XB^t0qmP;AbVQ|k^~=DMla z39rYu=UtjTxDPp_ciEpU&ny0nn$znJ%%Z}_9z`uMKEU+4)!X=B!LtHK9gaQH40I)* z=QBdjWwG!F{$_K{n)0f+x?){3^-(=WR~Met&R2uKEHL*4M}1m5b3+{*ywGn>nEu@P zo}PFM9PihJ=><=JFy9|;Rd`W*UBQf_0xv0Np=Tcdl~8BaszU1@dhoi3pML}1@|eCA zf4|Ja)4A@LbK{i-|6k0w_A9ZRaaO_k{;0}5fJ1FXIbOc?2HG_R9DbSwx1k(A#8<&> yDo0E0UL2mc74WRU?I?%itW8b8^R5CdHO@ufYxUrp?<=@V?hC(Q^grrwq4)&`UPl4| literal 0 HcmV?d00001 diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..6eeb1b7 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,103 @@ +use std::{collections::VecDeque, env::VarError, path::Path, sync::Arc}; + +use glam::{Quat, Vec3}; +use log::warn; + +use crate::{ + graphics::WlxGraphics, gui::font::FontCache, input::InputProvider, overlays::OverlayData, +}; + +pub const WATCH_DEFAULT_POS: Vec3 = Vec3::new(0., 0., 0.15); +pub const WATCH_DEFAULT_ROT: Quat = Quat::from_xyzw(0.7071066, 0., 0.7071066, 0.0007963); + +pub type Task = Box; + +pub struct AppState { + pub fc: FontCache, + //pub input: InputState, + pub session: AppSession, + pub tasks: VecDeque, + pub graphics: Arc, + pub format: vulkano::format::Format, + pub input: Box, +} + +pub struct AppSession { + pub config_path: String, + + pub show_screens: Vec, + pub show_keyboard: bool, + pub keyboard_volume: f32, + + pub screen_flip_h: bool, + pub screen_flip_v: bool, + pub screen_invert_color: bool, + + pub watch_hand: usize, + pub watch_pos: Vec3, + pub watch_rot: Quat, + + pub primary_hand: usize, + + pub capture_method: String, + + pub color_norm: Vec3, + pub color_shift: Vec3, + pub color_alt: Vec3, + pub color_grab: Vec3, + + pub click_freeze_time_ms: u64, +} + +impl AppSession { + pub fn load() -> AppSession { + let config_path = std::env::var("XDG_CONFIG_HOME") + .or_else(|_| std::env::var("HOME").map(|home| format!("{}/.config", home))) + .or_else(|_| { + warn!("Err: $XDG_CONFIG_HOME and $HOME are not set, using /tmp/wlxoverlay"); + Ok::("/tmp".to_string()) + }) + .map(|config| Path::new(&config).join("wlxoverlay")) + .ok() + .and_then(|path| path.to_str().map(|path| path.to_string())) + .unwrap(); + + let _ = std::fs::create_dir(&config_path); + + AppSession { + config_path, + show_screens: vec!["DP-3".to_string()], + keyboard_volume: 0.5, + show_keyboard: false, + screen_flip_h: false, + screen_flip_v: false, + screen_invert_color: false, + capture_method: "auto".to_string(), + primary_hand: 1, + watch_hand: 1, + watch_pos: WATCH_DEFAULT_POS, + watch_rot: WATCH_DEFAULT_ROT, + color_norm: Vec3 { + x: 0., + y: 1., + z: 1., + }, + color_shift: Vec3 { + x: 1., + y: 1., + z: 0., + }, + color_alt: Vec3 { + x: 1., + y: 0., + z: 1., + }, + color_grab: Vec3 { + x: 1., + y: 0., + z: 0., + }, + click_freeze_time_ms: 300, + } + } +}

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 OpmSCiEi27EWBv~p%11r` literal 0 HcmV?d00001 diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 0000000..569a4a2 --- /dev/null +++ b/src/input.rs @@ -0,0 +1,431 @@ +use glam::Vec2; +use idmap::{idmap, IdMap}; +use idmap_derive::IntegerId; +use input_linux::{ + AbsoluteAxis, AbsoluteInfo, AbsoluteInfoSetup, EventKind, InputId, Key, RelativeAxis, + UInputHandle, +}; +use libc::{input_event, timeval}; +use log::{error, info}; +use once_cell::sync::Lazy; +use std::fs::File; +use std::mem::transmute; +use strum::{EnumIter, EnumString, IntoEnumIterator}; + +pub fn initialize_input() -> Box { + if let Some(uinput) = UInputProvider::try_new() { + info!("Initialized uinput."); + return Box::new(uinput); + } + error!("Could not create uinput provider. Keyboard/Mouse input will not work!"); + error!("Check if you're in `input` group: `id -nG`"); + Box::new(DummyProvider {}) +} + +pub trait InputProvider { + fn mouse_move(&mut self, pos: Vec2); + fn send_button(&self, button: u16, down: bool); + fn wheel(&self, delta: i32); + fn set_modifiers(&mut self, mods: u8); + fn send_key(&self, key: u16, down: bool); + fn set_desktop_extent(&mut self, extent: Vec2); + fn on_new_frame(&mut self); +} + +pub struct UInputProvider { + handle: UInputHandle, + desktop_extent: Vec2, + mouse_moved: bool, + cur_modifiers: u8, +} + +pub struct DummyProvider; + +pub const MOUSE_LEFT: u16 = 0x110; +pub const MOUSE_RIGHT: u16 = 0x111; +pub const MOUSE_MIDDLE: u16 = 0x112; + +const MOUSE_EXTENT: f32 = 32768.; + +const EV_SYN: u16 = 0x0; +const EV_KEY: u16 = 0x1; +const EV_REL: u16 = 0x2; +const EV_ABS: u16 = 0x3; + +impl UInputProvider { + fn try_new() -> Option { + if let Ok(file) = File::create("/dev/uinput") { + let handle = UInputHandle::new(file); + + let id = InputId { + bustype: 0x03, + vendor: 0x4711, + product: 0x0819, + version: 5, + }; + + let name = b"WlxOverlay Keyboard-Mouse Hybrid Thing\0"; + + let abs_info = vec![ + AbsoluteInfoSetup { + axis: input_linux::AbsoluteAxis::X, + info: AbsoluteInfo { + value: 0, + minimum: 0, + maximum: MOUSE_EXTENT as _, + fuzz: 0, + flat: 0, + resolution: 10, + }, + }, + AbsoluteInfoSetup { + axis: input_linux::AbsoluteAxis::Y, + info: AbsoluteInfo { + value: 0, + minimum: 0, + maximum: MOUSE_EXTENT as _, + fuzz: 0, + flat: 0, + resolution: 10, + }, + }, + ]; + + if handle.set_evbit(EventKind::Key).is_err() { + return None; + } + if handle.set_evbit(EventKind::Absolute).is_err() { + return None; + } + if handle.set_evbit(EventKind::Relative).is_err() { + return None; + } + + for btn in MOUSE_LEFT..=MOUSE_MIDDLE { + let key: Key = unsafe { transmute(btn) }; + if handle.set_keybit(key).is_err() { + return None; + } + } + + for key in VirtualKey::iter() { + let key: Key = unsafe { transmute(key as u16) }; + if handle.set_keybit(key).is_err() { + return None; + } + } + + if handle.set_absbit(AbsoluteAxis::X).is_err() { + return None; + } + if handle.set_absbit(AbsoluteAxis::Y).is_err() { + return None; + } + if handle.set_relbit(RelativeAxis::Wheel).is_err() { + return None; + } + + if handle.create(&id, name, 0, &abs_info).is_ok() { + return Some(UInputProvider { + handle, + desktop_extent: Vec2::ZERO, + mouse_moved: false, + cur_modifiers: 0, + }); + } + } + None + } +} + +impl InputProvider for UInputProvider { + fn mouse_move(&mut self, pos: Vec2) { + if self.mouse_moved { + return; + } + self.mouse_moved = true; + + let pos = pos * (MOUSE_EXTENT / self.desktop_extent); + + let time = get_time(); + let events = [ + new_event(time, EV_ABS, AbsoluteAxis::X as _, pos.x as i32), + new_event(time, EV_ABS, AbsoluteAxis::Y as _, pos.y as i32), + new_event(time, EV_SYN, 0, 0), + ]; + if let Err(res) = self.handle.write(&events) { + error!("{}", res.to_string()); + } + } + fn send_button(&self, button: u16, down: bool) { + let time = get_time(); + let events = [ + new_event(time, EV_KEY, button, down as _), + new_event(time, EV_SYN, 0, 0), + ]; + if let Err(res) = self.handle.write(&events) { + error!("{}", res.to_string()); + } + } + fn wheel(&self, delta: i32) { + let time = get_time(); + let events = [ + new_event(time, EV_REL, RelativeAxis::Wheel as _, delta), + new_event(time, EV_SYN, 0, 0), + ]; + if let Err(res) = self.handle.write(&events) { + error!("{}", res.to_string()); + } + } + fn set_modifiers(&mut self, modifiers: u8) { + let changed = self.cur_modifiers ^ modifiers; + for i in 0..7 { + let m = 1 << i; + if changed & m != 0 { + let vk = MODS_TO_KEYS.get(m).unwrap()[0] as u16; + self.send_key(vk, modifiers & m != 0); + } + } + self.cur_modifiers = modifiers; + } + fn send_key(&self, key: u16, down: bool) { + let time = get_time(); + let events = [ + new_event(time, EV_KEY, key - 8, down as _), + new_event(time, EV_SYN, 0, 0), + ]; + if let Err(res) = self.handle.write(&events) { + error!("{}", res.to_string()); + } + } + fn set_desktop_extent(&mut self, extent: Vec2) { + info!("Desktop extent: {:?}", extent); + self.desktop_extent = extent; + } + fn on_new_frame(&mut self) { + self.mouse_moved = false; + } +} + +impl InputProvider for DummyProvider { + fn mouse_move(&mut self, _pos: Vec2) {} + fn send_button(&self, _button: u16, _down: bool) {} + fn wheel(&self, _delta: i32) {} + fn set_modifiers(&mut self, _modifiers: u8) {} + fn send_key(&self, _key: u16, _down: bool) {} + fn set_desktop_extent(&mut self, _extent: Vec2) {} + fn on_new_frame(&mut self) {} +} + +#[inline] +fn get_time() -> timeval { + let mut time = timeval { + tv_sec: 0, + tv_usec: 0, + }; + unsafe { libc::gettimeofday(&mut time, std::ptr::null_mut()) }; + time +} + +#[inline] +fn new_event(time: timeval, type_: u16, code: u16, value: i32) -> input_event { + input_event { + time, + type_, + code, + value, + } +} + +pub type KeyModifier = u8; +pub const SHIFT: KeyModifier = 0x01; +pub const CAPS_LOCK: KeyModifier = 0x02; +pub const CTRL: KeyModifier = 0x04; +pub const ALT: KeyModifier = 0x08; +pub const NUM_LOCK: KeyModifier = 0x10; +pub const SUPER: KeyModifier = 0x40; +pub const META: KeyModifier = 0x80; + +#[allow(non_camel_case_types)] +#[derive(Debug, PartialEq, Clone, Copy, IntegerId, EnumString, EnumIter)] +pub enum VirtualKey { + Escape = 9, + N1, // number row + N2, + N3, + N4, + N5, + N6, + N7, + N8, + N9, + N0, + Minus, + Plus, + BackSpace, + Tab, + Q, + W, + E, + R, + T, + Y, + U, + I, + O, + P, + Oem4, // [ { + Oem6, // ] } + Return, + LCtrl, + A, + S, + D, + F, + G, + H, + J, + K, + L, + Oem1, // ; : + Oem7, // ' " + Oem3, // ` ~ + LShift, + Oem5, // \ | + Z, + X, + C, + V, + B, + N, + M, + Comma, // , < + Period, // . > + Oem2, // / ? + RShift, + KP_Multiply, + LAlt, + Space, + Caps, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + NumLock, + Scroll, + KP_7, // KeyPad + KP_8, + KP_9, + KP_Subtract, + KP_4, + KP_5, + KP_6, + KP_Add, + KP_1, + KP_2, + KP_3, + KP_0, + KP_Decimal, + Oem102 = 94, // Optional key usually between LShift and Z + F11, + F12, + AbntC1, + Katakana, + Hiragana, + Henkan, + Kana, + Muhenkan, + KP_Enter = 104, + RCtrl, + KP_Divide, + Print, + Meta, // Right Alt aka AltGr + Home = 110, + Up, + Prior, + Left, + Right, + End, + Down, + Next, + Insert, + Delete, + XF86AudioMute = 121, + XF86AudioLowerVolume, + XF86AudioRaiseVolume, + Pause = 127, + AbntC2 = 129, + Hangul, + Hanja, + LSuper = 133, + RSuper, + Menu, + Help = 146, + XF86MenuKB, + XF86Sleep = 150, + XF86Xfer = 155, + XF86Launch1, + XF86Launch2, + XF86WWW, + XF86Mail = 163, + XF86Favorites, + XF86MyComputer, + XF86Back, + XF86Forward, + XF86AudioNext = 171, + XF86AudioPlay, + XF86AudioPrev, + XF86AudioStop, + XF86HomePage = 180, + XF86Reload, + F13 = 191, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + Hyper = 207, + XF86Launch3, + XF86Launch4, + XF86LaunchB, + XF86Search = 225, +} + +pub static KEYS_TO_MODS: Lazy> = Lazy::new(|| { + idmap! { + VirtualKey::LShift => SHIFT, + VirtualKey::RShift => SHIFT, + VirtualKey::Caps => CAPS_LOCK, + VirtualKey::LCtrl => CTRL, + VirtualKey::RCtrl => CTRL, + VirtualKey::LAlt => ALT, + VirtualKey::NumLock => NUM_LOCK, + VirtualKey::LSuper => SUPER, + VirtualKey::RSuper => SUPER, + VirtualKey::Meta => META, + } +}); + +pub static MODS_TO_KEYS: Lazy>> = Lazy::new(|| { + idmap! { + SHIFT => vec![VirtualKey::LShift, VirtualKey::RShift], + CAPS_LOCK => vec![VirtualKey::Caps], + CTRL => vec![VirtualKey::LCtrl, VirtualKey::RCtrl], + ALT => vec![VirtualKey::LAlt], + NUM_LOCK => vec![VirtualKey::NumLock], + SUPER => vec![VirtualKey::LSuper, VirtualKey::RSuper], + META => vec![VirtualKey::Meta], + } +}); diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fac077a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,271 @@ +#[allow(dead_code)] +mod backend; +mod graphics; +mod gui; +mod input; +mod overlays; +mod ovr; +mod shaders; +mod state; + +use std::collections::VecDeque; +use std::sync::Arc; + +use crate::graphics::{Vert2Uv, WlxGraphics, INDICES}; +use crate::input::initialize_input; +use crate::overlays::watch::create_watch; +use crate::{ + shaders::{frag_sprite, vert_common}, + state::AppState, +}; +use env_logger::Env; +use log::{info, warn}; +use vulkano::{ + buffer::BufferUsage, + command_buffer::CommandBufferUsage, + image::{ + view::{ImageView, ImageViewCreateInfo}, + ImageAccess, ImageSubresourceRange, ImageViewType, SwapchainImage, + }, + pipeline::graphics::viewport::Viewport, + sampler::Filter, + swapchain::{ + acquire_next_image, AcquireError, SwapchainCreateInfo, SwapchainCreationError, + SwapchainPresentInfo, + }, + sync::{self, FlushError, GpuFuture}, +}; +use winit::{ + event::{Event, WindowEvent}, + event_loop::ControlFlow, + window::Window, +}; +use wlx_capture::{frame::WlxFrame, wayland::WlxClient, wlr::WlrDmabufCapture, WlxCapture}; + +fn main() { + env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + info!( + "Welcome to {} version {}!", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ); + + let (graphics, event_loop) = WlxGraphics::new(); + let (mut swapchain, images) = graphics.create_swapchain(None); + + let mut app = AppState { + fc: crate::gui::font::FontCache::new(), + session: crate::state::AppSession::load(), + tasks: VecDeque::with_capacity(16), + graphics: graphics.clone(), + format: swapchain.image_format(), + input: initialize_input(), + }; + + let wl = WlxClient::new().unwrap(); + let output_id = wl.outputs[0].id; + let mut capture = WlrDmabufCapture::new(wl, output_id).unwrap(); + let rx = capture.init(); + + 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 vertex_buffer = graphics.upload_buffer(BufferUsage::VERTEX_BUFFER, vertices.iter()); + let index_buffer = graphics.upload_buffer(BufferUsage::INDEX_BUFFER, INDICES.iter()); + + let vs = vert_common::load(graphics.device.clone()).unwrap(); + let fs = frag_sprite::load(graphics.device.clone()).unwrap(); + + let uploads = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit); + + let mut watch = create_watch(&app, vec![]); + watch.init(&mut app); + watch.render(&mut app); + + let pipeline1 = graphics.create_pipeline(vs.clone(), fs.clone(), swapchain.image_format()); + let set1 = pipeline1.uniform_sampler(0, watch.view(), Filter::Nearest); + + capture.request_new_frame(); + + let pipeline = graphics.create_pipeline(vs, fs, swapchain.image_format()); + let set0; + loop { + if let Ok(frame) = rx.try_recv() { + match frame { + WlxFrame::Dmabuf(dmabuf_frame) => match graphics.dmabuf_texture(dmabuf_frame) { + Ok(tex) => { + let format = tex.format(); + let view = ImageView::new( + tex, + ImageViewCreateInfo { + format: Some(format), + view_type: ImageViewType::Dim2d, + subresource_range: ImageSubresourceRange::from_parameters( + format, 1, 1, + ), + ..Default::default() + }, + ) + .unwrap(); + set0 = pipeline.uniform_sampler(0, view, Filter::Nearest); + break; + } + Err(e) => { + warn!("Failed to create texture from dmabuf: {}", e); + } + }, + _ => { + warn!("Received non-dmabuf frame"); + } + } + } + } + + //let set1 = graphics.uniform_buffer(1, vec![1.0, 1.0, 1.0, 1.0]); + let image_extent_f32 = [ + swapchain.image_extent()[0] as f32, + swapchain.image_extent()[1] as f32, + ]; + let image_extent2_f32 = [ + swapchain.image_extent()[0] as f32 / 2., + swapchain.image_extent()[1] as f32 / 2., + ]; + let pass = pipeline.create_pass( + image_extent_f32, + vertex_buffer.clone(), + index_buffer.clone(), + vec![set0], + ); + let pass2 = pipeline1.create_pass(image_extent2_f32, vertex_buffer, index_buffer, vec![set1]); + + let mut viewport = Viewport { + origin: [0.0, 0.0], + dimensions: [1024.0, 1024.0], + depth_range: 0.0..1.0, + }; + + let mut attachment_image_views = window_size_dependent_setup(&images, &mut viewport); + + //let set1 = pipeline.uniform_buffer(1, vec![1.0, 0.0, 1.0, 1.0]); + + let mut recreate_swapchain = false; + let mut previous_frame_end = //Some(sync::now(graphics.device.clone()).boxed()); + Some(uploads.end_and_execute().boxed()); + + event_loop.run(move |event, _, control_flow| match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + *control_flow = ControlFlow::Exit; + } + Event::WindowEvent { + event: WindowEvent::Resized(_), + .. + } => { + recreate_swapchain = true; + } + Event::RedrawEventsCleared => { + previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if recreate_swapchain { + let window = graphics + .surface + .object() + .unwrap() + .downcast_ref::() + .unwrap(); + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: window.inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("failed to recreate swapchain: {e}"), + }; + + swapchain = new_swapchain; + attachment_image_views = window_size_dependent_setup(&new_images, &mut viewport); + recreate_swapchain = false; + } + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(swapchain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + recreate_swapchain = true; + } + + let cmd = graphics + .create_command_buffer(CommandBufferUsage::OneTimeSubmit) + .begin(attachment_image_views[image_index as usize].clone()) + .run(&pass) + .run(&pass2) + .end_render(); + + let future = previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(graphics.queue.clone(), cmd) + .unwrap() + .then_swapchain_present( + graphics.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + previous_frame_end = Some(future.boxed()); + } + Err(FlushError::OutOfDate) => { + recreate_swapchain = true; + previous_frame_end = Some(sync::now(graphics.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + previous_frame_end = Some(sync::now(graphics.device.clone()).boxed()); + } + } + } + _ => (), + }); +} + +/// This function is called once during initialization, then again whenever the window is resized. +fn window_size_dependent_setup( + images: &[Arc], + viewport: &mut Viewport, +) -> Vec>> { + let dimensions = images[0].dimensions().width_height(); + viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32]; + + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} diff --git a/src/main.rs.bak2 b/src/main.rs.bak2 new file mode 100644 index 0000000..8630400 --- /dev/null +++ b/src/main.rs.bak2 @@ -0,0 +1,159 @@ +#[allow(dead_code)] +#[allow(unused_imports)] + +mod graphics; +//mod gui; +//mod interactions; +//mod overlay; +//mod state; + +use core::slice; + +use ash::vk; +use env_logger::Env; +use glam::f32::Vec4; +use log::{info, warn}; +use wlx_capture::{wlr::WlrDmabufCapture, wayland::WlxClient, WlxCapture, frame::WlxFrame}; + +use crate::{graphics::{VkGraphics, VkDescriptor}}; + +fn main() { + env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + info!("Welcome to {} version {}!", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + +// let mut app = AppState { +// fc: gui::font::FontCache::new(), +// session: state::AppSession::load(), +// }; + + let wl = WlxClient::new().unwrap(); + let output_id = wl.outputs[0].id; + let mut capture = WlrDmabufCapture::new(wl, output_id).unwrap(); + let rx = capture.init(); + + let gfx = VkGraphics::new(); + let vert = gfx.create_shader(include_bytes!("shaders/vert-common.spv"), None); + let frag_sprite = gfx.create_shader( + include_bytes!("shaders/frag-sprite.spv"), + Some(vec![vk::DescriptorType::COMBINED_IMAGE_SAMPLER]) + ); + let frag_srgb = gfx.create_shader( + include_bytes!("shaders/frag-srgb.spv"), + Some(vec![vk::DescriptorType::COMBINED_IMAGE_SAMPLER]) + ); + let frag_glyph = gfx.create_shader( + include_bytes!("shaders/frag-glyph.spv"), + Some(vec![vk::DescriptorType::COMBINED_IMAGE_SAMPLER, vk::DescriptorType::UNIFORM_BUFFER]) + ); + let frag_color = gfx.create_shader( + include_bytes!("shaders/frag-color.spv"), + Some(vec![vk::DescriptorType::UNIFORM_BUFFER]) + ); + + let pipeline = gfx.create_pipeline(vert, frag_color, gfx.swapchain_format); + + let color_buf = gfx.create_buffer(vk::BufferUsageFlags::UNIFORM_BUFFER, &[Vec4::new(1.0, 0.0, 1.0, 1.0); 16]); + + gfx.render_loop(|| { + let frame = gfx.create_frame(); + + unsafe { gfx.device.reset_command_pool(frame.command_pool, vk::CommandPoolResetFlags::RELEASE_RESOURCES) }.unwrap(); + + let (present_index, _) = unsafe { + gfx.swapchain_loader.acquire_next_image( + gfx.swapchain, + std::u64::MAX, + frame.present_semaphore, + vk::Fence::null() + ).unwrap() + }; + + let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT) + .build(); + + unsafe { gfx.device.begin_command_buffer(frame.command_buffer, &command_buffer_begin_info) }.unwrap(); + + let image = gfx.present_images[present_index as usize]; + + let image_memory_barrier = vk::ImageMemoryBarrier2::builder() + .src_stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE) + .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .old_layout(vk::ImageLayout::UNDEFINED) + .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .image(image) + .subresource_range(vk::ImageSubresourceRange::builder().aspect_mask(vk::ImageAspectFlags::COLOR).level_count(1).layer_count(1).build()) + .build(); + + unsafe { gfx.device.cmd_pipeline_barrier2(frame.command_buffer, &vk::DependencyInfo::builder().image_memory_barriers(slice::from_ref(&image_memory_barrier)).build()) }; + + let color_attachment = vk::RenderingAttachmentInfo::builder() + .image_view(gfx.present_image_views[present_index as usize]) + .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .load_op(vk::AttachmentLoadOp::CLEAR) + .store_op(vk::AttachmentStoreOp::STORE) + .clear_value(vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.0, 0.0, 0.0, 1.0] + } + }); + + let rendering_info = vk::RenderingInfo::builder() + .render_area(vk::Rect2D::builder().extent(vk::Extent2D::builder().width(1600).height(900).build()).build()) + .layer_count(1) + .color_attachments(slice::from_ref(&color_attachment)) + .build(); + + + unsafe { gfx.device.cmd_begin_rendering(frame.command_buffer, &rendering_info) }; + + let descriptor = VkDescriptor::Buffer(color_buf.clone()); + pipeline.bind_descriptors(slice::from_ref(&descriptor)); + pipeline.render(&frame, vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: vk::Extent2D { width: 1600, height: 900 } + }); + + unsafe { gfx.device.cmd_end_rendering(frame.command_buffer) }; + + + let image_memory_barrier = vk::ImageMemoryBarrier2::builder() + .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .dst_stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE) + .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) + .image(image) + .subresource_range(vk::ImageSubresourceRange::builder().aspect_mask(vk::ImageAspectFlags::COLOR).level_count(1).layer_count(1).build()) + .build(); + + unsafe { + gfx.device.cmd_pipeline_barrier2(frame.command_buffer, &vk::DependencyInfo::builder().image_memory_barriers(slice::from_ref(&image_memory_barrier)).build()); + }; + + unsafe { gfx.device.end_command_buffer(frame.command_buffer) }.unwrap(); + + let wait_semaphores = [frame.present_semaphore]; + let wait_dst_stage_mask = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; + + let submit_info = vk::SubmitInfo::builder() + .wait_semaphores(&wait_semaphores) + .wait_dst_stage_mask(&wait_dst_stage_mask) + .command_buffers(slice::from_ref(&frame.command_buffer)) + .signal_semaphores(slice::from_ref(&frame.render_semaphore)); + + unsafe { gfx.device.queue_submit(gfx.present_queue, slice::from_ref(&submit_info), frame.fence) }.unwrap(); + + let present_info = vk::PresentInfoKHR::builder() + .wait_semaphores(slice::from_ref(&frame.present_semaphore)) + .swapchains(slice::from_ref(&gfx.swapchain)) + .image_indices(slice::from_ref(&present_index)) + .build(); + + unsafe { gfx.swapchain_loader.queue_present(gfx.present_queue, &present_info).unwrap() }; + }); + + unsafe { gfx.device.device_wait_idle().unwrap() }; +} + diff --git a/src/overlays/interactions.rs b/src/overlays/interactions.rs new file mode 100644 index 0000000..6f05226 --- /dev/null +++ b/src/overlays/interactions.rs @@ -0,0 +1,41 @@ +use std::{collections::VecDeque, time::Instant}; + +use glam::{Affine3A, Vec2, Vec3}; + +use crate::state::AppState; + +pub const HAND_LEFT: usize = 0; +pub const HAND_RIGHT: usize = 1; + +pub const POINTER_NORM: u16 = 0; +pub const POINTER_SHIFT: u16 = 1; +pub const POINTER_ALT: u16 = 2; + +pub trait InteractionHandler { + fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit); + fn on_left(&mut self, app: &mut AppState, hand: usize); + fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool); + fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32); +} + +// --- Dummies & plumbing below --- + +impl Default for PointerState { + fn default() -> Self { + Self { + click: false, + grab: false, + show_hide: false, + scroll: 0., + } + } +} + +pub struct DummyInteractionHandler; + +impl InteractionHandler for DummyInteractionHandler { + fn on_left(&mut self, _app: &mut AppState, _hand: usize) {} + fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) {} + fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {} + fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {} +} diff --git a/src/overlays/keyboard.rs b/src/overlays/keyboard.rs new file mode 100644 index 0000000..63540ac --- /dev/null +++ b/src/overlays/keyboard.rs @@ -0,0 +1,354 @@ +use std::{ + collections::HashMap, + env::var, + fs, + io::Cursor, + path::PathBuf, + process::{Child, Command}, + str::FromStr, + sync::Arc, +}; + +use crate::{ + gui::{color_parse, CanvasBuilder, Control}, + input::{KeyModifier, VirtualKey, KEYS_TO_MODS}, + state::AppState, +}; +use glam::{vec2, vec3}; +use log::error; +use once_cell::sync::Lazy; +use regex::Regex; +use rodio::{Decoder, OutputStream, Source}; +use serde::{Deserialize, Serialize}; + +use super::OverlayData; + +const PIXELS_PER_UNIT: f32 = 80.; +const BUTTON_PADDING: f32 = 4.; + +pub fn create_keyboard(app: &AppState) -> OverlayData { + let size = vec2( + LAYOUT.row_size * PIXELS_PER_UNIT, + (LAYOUT.main_layout.len() as f32) * PIXELS_PER_UNIT, + ); + + let data = KeyboardData { + modifiers: 0, + processes: vec![], + audio_stream: None, + }; + + let mut canvas = CanvasBuilder::new( + size.x as _, + size.y as _, + app.graphics.clone(), + app.format, + data, + ); + + canvas.bg_color = color_parse("#101010"); + canvas.panel(0., 0., size.x, size.y); + + canvas.font_size = 18; + canvas.bg_color = color_parse("#202020"); + + let unit_size = size.x / LAYOUT.row_size; + let h = unit_size - 2. * BUTTON_PADDING; + + for row in 0..LAYOUT.key_sizes.len() { + let y = unit_size * (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 * sum_size + BUTTON_PADDING; + let w = unit_size * my_size - 2. * BUTTON_PADDING; + + if let Some(key) = LAYOUT.main_layout[row][col].as_ref() { + let mut maybe_state: Option = None; + if let Ok(vk) = VirtualKey::from_str(key) { + if let Some(mods) = KEYS_TO_MODS.get(vk) { + maybe_state = Some(KeyButtonData::Modifier { + modifier: *mods, + sticky: false, + pressed: 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) { + maybe_state = Some(KeyButtonData::Exec { + program: exec_args.first().unwrap().clone(), + args: exec_args.iter().skip(1).cloned().collect(), + }); + } else { + error!("Unknown key: {}", key); + } + + if let Some(state) = maybe_state { + let label = LAYOUT.label_for_key(key); + let button = canvas.key_button(x, y, w, h, &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(); + + OverlayData { + name: Arc::from("Kbd"), + show_hide: true, + width: LAYOUT.row_size * 0.05, + size: (size.x as _, size.y as _), + grabbable: true, + spawn_point: vec3(0., -0.5, -1.), + backend: Box::new(canvas), + ..Default::default() + } +} + +fn key_press( + control: &mut Control, + data: &mut KeyboardData, + app: &mut AppState, +) { + match control.state.as_mut() { + Some(KeyButtonData::Key { vk, pressed }) => { + data.key_click(); + app.input.send_key(*vk as _, true); + *pressed = true; + } + Some(KeyButtonData::Modifier { + modifier, + sticky, + pressed, + }) => { + *sticky = data.modifiers & *modifier == 0; + data.modifiers |= *modifier; + data.key_click(); + app.input.set_modifiers(data.modifiers); + *pressed = true; + } + Some(KeyButtonData::Macro { verbs }) => { + data.key_click(); + for (vk, press) in verbs { + app.input.send_key(*vk as _, *press); + } + } + Some(KeyButtonData::Exec { program, args }) => { + // Reap previous processes + data.processes + .retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_)))); + + data.key_click(); + 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 }) => { + app.input.send_key(*vk as _, false); + *pressed = false; + } + Some(KeyButtonData::Modifier { + modifier, + sticky, + pressed, + }) => { + if !*sticky { + data.modifiers &= !*modifier; + app.input.set_modifiers(data.modifiers); + *pressed = false; + } + } + _ => {} + } +} + +fn test_highlight( + control: &Control, + _data: &mut KeyboardData, + _app: &mut AppState, +) -> bool { + match control.state.as_ref() { + Some(KeyButtonData::Key { pressed, .. }) => *pressed, + Some(KeyButtonData::Modifier { pressed, .. }) => *pressed, + _ => false, + } +} + +struct KeyboardData { + modifiers: KeyModifier, + processes: Vec, + audio_stream: Option, +} + +impl KeyboardData { + fn key_click(&mut self) { + let wav = include_bytes!("../res/421581.wav"); + let cursor = Cursor::new(wav); + let source = Decoder::new_wav(cursor).unwrap(); + self.audio_stream = None; + if let Ok((stream, handle)) = OutputStream::try_default() { + let _ = handle.play_raw(source.convert_samples()); + self.audio_stream = Some(stream); + } else { + error!("Failed to play key click"); + } + } +} + +enum KeyButtonData { + Key { + vk: VirtualKey, + pressed: bool, + }, + Modifier { + modifier: KeyModifier, + sticky: bool, + pressed: bool, + }, + Macro { + verbs: Vec<(VirtualKey, bool)>, + }, + Exec { + program: String, + args: Vec, + }, +} + +static KEYBOARD_YAML: Lazy = Lazy::new(|| { + let home = &var("HOME").unwrap(); + [home, ".config/wlxoverlay/keyboard.yaml"].iter().collect() //TODO other paths +}); + +static LAYOUT: Lazy = Lazy::new(Layout::load_from_disk); + +static MACRO_REGEX: Lazy = + Lazy::new(|| Regex::new(r"^([A-Za-z0-1_-]+)(?: +(UP|DOWN))?$").unwrap()); + +#[derive(Debug, Deserialize, Serialize)] +struct Layout { + name: String, + row_size: f32, + key_sizes: Vec>, + main_layout: Vec>>, + exec_commands: HashMap>, + macros: HashMap>, + labels: HashMap>, +} + +impl Layout { + fn load_from_disk() -> Layout { + let mut yaml = fs::read_to_string(KEYBOARD_YAML.as_path()).ok(); + + if yaml.is_none() { + yaml = Some(include_str!("../res/keyboard.yaml").to_string()); + } + + let mut layout: Layout = + serde_yaml::from_str(&yaml.unwrap()).expect("Failed to parse keyboard.yaml"); + 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(); + if (width - self.row_size).abs() > 0.001 { + panic!( + "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(); + if width != self.key_sizes[i].len() { + panic!( + "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(); + } + vec![format!( + "{}{}", + key.chars().next().unwrap().to_uppercase(), + &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 { + 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 { + error!("Unknown virtual key: {}", &caps[1]); + return vec![]; + } + } + } + key_events +} diff --git a/src/overlays/mod.rs b/src/overlays/mod.rs new file mode 100644 index 0000000..a49a8a9 --- /dev/null +++ b/src/overlays/mod.rs @@ -0,0 +1,145 @@ +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; + +use glam::{Affine3A, Quat, Vec3}; +use vulkano::image::ImageViewAbstract; + +use crate::state::AppState; + +use self::interactions::{DummyInteractionHandler, InteractionHandler, PointerHit}; + +pub mod interactions; +pub mod keyboard; +pub mod watch; + +static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0); + +pub enum RelativeTo { + None, + Head, + Hand(usize), +} + +pub trait OverlayBackend: OverlayRenderer + InteractionHandler {} + +pub struct OverlayData { + pub id: usize, + pub name: Arc, + pub width: f32, + pub size: (i32, i32), + pub want_visible: bool, + pub show_hide: bool, + pub grabbable: bool, + pub transform: Affine3A, + pub spawn_point: Vec3, + pub spawn_rotation: Quat, + pub relative_to: RelativeTo, + pub interaction_transform: Affine3A, + pub backend: Box, + pub primary_pointer: Option, +} +impl Default for OverlayData { + fn default() -> OverlayData { + OverlayData { + id: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed), + name: Arc::from(""), + width: 1., + size: (0, 0), + want_visible: false, + show_hide: false, + grabbable: false, + relative_to: RelativeTo::None, + spawn_point: Vec3::NEG_Z, + spawn_rotation: Quat::IDENTITY, + transform: Affine3A::IDENTITY, + interaction_transform: Affine3A::IDENTITY, + backend: Box::new(SplitOverlayBackend::default()), + primary_pointer: None, + } + } +} + +impl OverlayData { + pub fn reset(&mut self, app: &mut AppState) { + todo!() + } + pub fn init(&mut self, app: &mut AppState) { + self.backend.init(app); + } + pub fn render(&mut self, app: &mut AppState) { + self.backend.render(app); + } + pub fn view(&mut self) -> Arc { + self.backend.view() + } +} + +pub trait OverlayRenderer { + fn init(&mut self, app: &mut AppState); + fn pause(&mut self, app: &mut AppState); + fn resume(&mut self, app: &mut AppState); + fn render(&mut self, app: &mut AppState); + fn view(&mut self) -> Arc; +} + +pub struct FallbackRenderer; + +impl OverlayRenderer for FallbackRenderer { + fn init(&mut self, _app: &mut AppState) {} + fn pause(&mut self, _app: &mut AppState) {} + fn resume(&mut self, _app: &mut AppState) {} + fn render(&mut self, _app: &mut AppState) {} + fn view(&mut self) -> Arc { + unimplemented!() + } +} +// Boilerplate and dummies + +pub struct SplitOverlayBackend { + pub renderer: Box, + pub interaction: Box, +} + +impl Default for SplitOverlayBackend { + fn default() -> SplitOverlayBackend { + SplitOverlayBackend { + renderer: Box::new(FallbackRenderer), + interaction: Box::new(DummyInteractionHandler), + } + } +} + +impl OverlayBackend for SplitOverlayBackend {} +impl OverlayRenderer for SplitOverlayBackend { + fn init(&mut self, app: &mut AppState) { + self.renderer.init(app); + } + fn pause(&mut self, app: &mut AppState) { + self.renderer.pause(app); + } + fn resume(&mut self, app: &mut AppState) { + self.renderer.resume(app); + } + fn render(&mut self, app: &mut AppState) { + self.renderer.render(app); + } + fn view(&mut self) -> Arc { + self.renderer.view() + } +} +impl InteractionHandler for SplitOverlayBackend { + fn on_left(&mut self, app: &mut AppState, hand: usize) { + self.interaction.on_left(app, hand); + } + fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) { + self.interaction.on_hover(app, hit); + } + fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) { + self.interaction.on_scroll(app, hit, delta); + } + fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { + self.interaction.on_pointer(app, hit, pressed); + } +} diff --git a/src/overlays/screen.rs b/src/overlays/screen.rs new file mode 100644 index 0000000..7256607 --- /dev/null +++ b/src/overlays/screen.rs @@ -0,0 +1,31 @@ + +pub struct ScreenInteractionData { + next_scroll: Instant, + next_move: Instant, + mouse_transform: Affine2, +} +impl ScreenInteractionData { + fn new(pos: Vec2, size: Vec2, transform: Transform) -> ScreenInteractionHandler { + 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), + }; + + ScreenInteractionHandler { + next_scroll: Instant::now(), + next_move: Instant::now(), + mouse_transform: transform, + } + } +} + +struct ScreenInteractionHandler { + +} + diff --git a/src/overlays/watch.rs b/src/overlays/watch.rs new file mode 100644 index 0000000..98922e0 --- /dev/null +++ b/src/overlays/watch.rs @@ -0,0 +1,170 @@ +use std::{sync::Arc, time::Instant}; + +use chrono::Local; +use glam::{Quat, Vec3}; + +use crate::{ + gui::{color_parse, CanvasBuilder}, + state::AppState, +}; + +use super::{OverlayData, RelativeTo}; + +pub const WATCH_DEFAULT_POS: Vec3 = Vec3::new(0., 0., 0.15); +pub const WATCH_DEFAULT_ROT: Quat = Quat::from_xyzw(0.7071066, 0., 0.7071066, 0.0007963); + +pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc)>) -> OverlayData { + let mut canvas = CanvasBuilder::new(400, 200, state.graphics.clone(), state.format, ()); + let empty_str: Arc = Arc::from(""); + + // Background + canvas.bg_color = color_parse("#353535"); + canvas.panel(0., 0., 400., 200.); + + // Time display + canvas.font_size = 46; + let clock = canvas.label(19., 100., 200., 50., empty_str.clone()); + clock.on_update = Some(|control, _data, _app| { + let date = Local::now(); + control.set_text(&format!("{}", &date.format("%H:%M"))); + }); + + canvas.font_size = 14; + let date = canvas.label(20., 125., 200., 50., empty_str.clone()); + date.on_update = Some(|control, _data, _app| { + let date = Local::now(); + control.set_text(&format!("{}", &date.format("%x"))); + }); + + let day_of_week = canvas.label(20., 150., 200., 50., empty_str); + day_of_week.on_update = Some(|control, _data, _app| { + let date = Local::now(); + control.set_text(&format!("{}", &date.format("%A"))); + }); + + // Volume controls + canvas.bg_color = color_parse("#222222"); + canvas.fg_color = color_parse("#AAAAAA"); + canvas.font_size = 14; + + canvas.bg_color = color_parse("#303030"); + canvas.fg_color = color_parse("#353535"); + + let vol_up = canvas.button(327., 116., 46., 32., "+".into()); + vol_up.on_press = Some(|_control, _data, _app| { + println!("Volume up!"); //TODO + }); + + let vol_dn = canvas.button(327., 52., 46., 32., "-".into()); + vol_dn.on_press = Some(|_control, _data, _app| { + println!("Volume down!"); //TODO + }); + + canvas.bg_color = color_parse("#303030"); + canvas.fg_color = color_parse("#353535"); + + let settings = canvas.button(2., 162., 36., 36., "☰".into()); + settings.on_press = Some(|_control, _data, _app| { + println!("Settings!"); //TODO + }); + + canvas.fg_color = color_parse("#CCBBAA"); + canvas.bg_color = color_parse("#406050"); + // Bottom row + let num_buttons = screens.len() + 1; + let button_width = 360. / num_buttons as f32; + let mut button_x = 40.; + + let keyboard = canvas.button(button_x + 2., 162., button_width - 4., 36., "Kbd".into()); + keyboard.state = Some(WatchButtonState { + pressed_at: Instant::now(), + scr_idx: 0, + }); + + keyboard.on_press = Some(|control, _data, _app| { + if let Some(state) = control.state.as_mut() { + state.pressed_at = Instant::now(); + } + }); + keyboard.on_release = Some(|control, _data, app| { + if let Some(state) = control.state.as_ref() { + if Instant::now() + .saturating_duration_since(state.pressed_at) + .as_millis() + < 2000 + { + app.tasks.push_back(Box::new(|_app, o| { + for overlay in o { + if &*overlay.name == "Kbd" { + overlay.want_visible = !overlay.want_visible; + return; + } + } + })); + } else { + app.tasks.push_back(Box::new(|app, o| { + for overlay in o { + if &*overlay.name == "Kbd" { + overlay.reset(app); + } + } + })); + } + } + }); + button_x += button_width; + + canvas.bg_color = color_parse("#405060"); + + for (scr_idx, scr_name) in screens.into_iter() { + let button = canvas.button(button_x + 2., 162., button_width - 4., 36., scr_name); + button.state = Some(WatchButtonState { + pressed_at: Instant::now(), + scr_idx, + }); + + button.on_press = Some(|control, _data, _app| { + if let Some(state) = control.state.as_mut() { + state.pressed_at = Instant::now(); + } + }); + button.on_release = Some(|control, _data, app| { + if let Some(state) = control.state.as_ref() { + let scr_idx = state.scr_idx; + if Instant::now() + .saturating_duration_since(state.pressed_at) + .as_millis() + < 2000 + { + app.tasks.push_back(Box::new(move |_app, o| { + o[scr_idx].want_visible = !o[scr_idx].want_visible; + })); + } else { + app.tasks.push_back(Box::new(move |app, o| { + o[scr_idx].reset(app); + })); + } + } + }); + button_x += button_width; + } + + let relative_to = RelativeTo::Hand(state.session.watch_hand); + + OverlayData { + name: "Watch".into(), + size: (400, 200), + width: 0.065, + backend: Box::new(canvas.build()), + want_visible: true, + relative_to, + spawn_point: state.session.watch_pos, + spawn_rotation: state.session.watch_rot, + ..Default::default() + } +} + +struct WatchButtonState { + pressed_at: Instant, + scr_idx: usize, +} diff --git a/src/ovr.rs b/src/ovr.rs new file mode 100644 index 0000000..9831f5d --- /dev/null +++ b/src/ovr.rs @@ -0,0 +1,64 @@ +use std::{path::Path, sync::Arc}; + +use vulkano::{ + image::{ + sys::{Image, RawImage}, + ImageViewAbstract, + }, + Handle, VulkanObject, +}; + +use crate::graphics::WlxGraphics; + +pub struct OpenVrState { + pub context: ovr_overlay::Context, +} + + +pub struct OvrTextureData { + image_handle: u64, + device: u64, + physical: u64, + instance: u64, + queue: u64, + queue_family_index: u32, + width: u32, + height: u32, + format: u32, + sample_count: u32, +} + +impl OvrTextureData { + pub fn new(graphics: Arc, view: Arc) -> OvrTextureData { + let image = view.image(); + + let device = graphics.device.handle().as_raw(); + let physical = graphics.device.physical_device().handle().as_raw(); + let instance = graphics.instance.handle().as_raw(); + let queue = graphics.queue.handle().as_raw(); + let queue_family_index = graphics.queue.queue_family_index(); + + let (width, height) = { + let dim = image.dimensions(); + (dim.width() as u32, dim.height() as u32) + }; + + let sample_count = image.samples() as u32; + let format = image.format() as u32; + + let image_handle = image.inner().image.handle().as_raw(); + + OvrTextureData { + image_handle, + device, + physical, + instance, + queue, + queue_family_index, + width, + height, + format, + sample_count, + } + } +} diff --git a/src/res/421581.wav b/src/res/421581.wav new file mode 100644 index 0000000000000000000000000000000000000000..11b767e4dc85daa5994f728eccbaf7ceefd2d2e8 GIT binary patch literal 11214 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 Z=QN5-?A|B6)zHCxv-n+^&;Qry{{TDh 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 sYE=Nva2$$ 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