From e5ab46be9104c7aa176642fbabd5c6f880a763b7 Mon Sep 17 00:00:00 2001
From: galister <22305755+galister@users.noreply.github.com>
Date: Fri, 10 Nov 2023 09:15:37 +0900
Subject: [PATCH] initial commit
---
Cargo.lock | 363 +++++++++-----
Cargo.toml | 4 +-
LICENSE | 674 ++++++++++++++++++++++++++
README.md | 4 +
src/backend/common.rs | 270 ++++++-----
src/backend/input.rs | 362 ++++++++++++++
src/backend/mod.rs | 2 +
src/backend/openvr/input.rs | 34 +-
src/backend/openvr/mod.rs | 103 +++-
src/backend/openvr/overlay.rs | 214 +++++++-
src/backend/overlay.rs | 170 +++++++
src/graphics.rs | 519 +++++++++++++++-----
src/gui/font.rs | 39 +-
src/gui/mod.rs | 43 +-
src/main.rs | 254 +---------
src/overlays/interactions.rs | 41 --
src/overlays/keyboard.rs | 25 +-
src/overlays/mod.rs | 144 +-----
src/overlays/screen.rs | 276 ++++++++++-
src/overlays/watch.rs | 89 ++--
src/ovr.rs | 64 ---
src/res/actions_binding_knuckles.json | 2 +-
src/res/actions_binding_oculus.json | 2 +-
src/res/actions_binding_vive.json | 2 +-
src/state.rs | 35 +-
25 files changed, 2721 insertions(+), 1014 deletions(-)
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 src/backend/input.rs
create mode 100644 src/backend/overlay.rs
delete mode 100644 src/overlays/interactions.rs
delete mode 100644 src/ovr.rs
diff --git a/Cargo.lock b/Cargo.lock
index dac4990d..641b97b2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -207,16 +207,29 @@ dependencies = [
]
[[package]]
-name = "async-executor"
-version = "1.6.0"
+name = "async-channel"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0"
+checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c"
dependencies = [
- "async-lock 2.8.0",
+ "concurrent-queue",
+ "event-listener 4.0.0",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c"
+dependencies = [
+ "async-lock 3.2.0",
"async-task",
"concurrent-queue",
"fastrand 2.0.1",
- "futures-lite 1.13.0",
+ "futures-lite 2.1.0",
"slab",
]
@@ -234,16 +247,16 @@ dependencies = [
[[package]]
name = "async-global-executor"
-version = "2.3.1"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
+checksum = "9b4353121d5644cdf2beb5726ab752e79a8db1ebb52031770ec47db31d245526"
dependencies = [
- "async-channel",
+ "async-channel 2.1.1",
"async-executor",
- "async-io 1.13.0",
- "async-lock 2.8.0",
+ "async-io 2.2.1",
+ "async-lock 3.2.0",
"blocking",
- "futures-lite 1.13.0",
+ "futures-lite 2.1.0",
"once_cell",
]
@@ -269,22 +282,21 @@ dependencies = [
[[package]]
name = "async-io"
-version = "2.2.0"
+version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997"
+checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff"
dependencies = [
- "async-lock 3.0.0",
+ "async-lock 3.2.0",
"cfg-if",
"concurrent-queue",
"futures-io",
- "futures-lite 2.0.1",
+ "futures-lite 2.1.0",
"parking",
- "polling 3.3.0",
- "rustix 0.38.21",
+ "polling 3.3.1",
+ "rustix 0.38.26",
"slab",
"tracing",
- "waker-fn",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -298,11 +310,11 @@ dependencies = [
[[package]]
name = "async-lock"
-version = "3.0.0"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45e900cdcd39bb94a14487d3f7ef92ca222162e6c7c3fe7cb3550ea75fb486ed"
+checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c"
dependencies = [
- "event-listener 3.0.1",
+ "event-listener 4.0.0",
"event-listener-strategy",
"pin-project-lite",
]
@@ -318,9 +330,9 @@ dependencies = [
"async-signal",
"blocking",
"cfg-if",
- "event-listener 3.0.1",
+ "event-listener 3.1.0",
"futures-lite 1.13.0",
- "rustix 0.38.21",
+ "rustix 0.38.26",
"windows-sys 0.48.0",
]
@@ -341,13 +353,13 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
dependencies = [
- "async-io 2.2.0",
+ "async-io 2.2.1",
"async-lock 2.8.0",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
- "rustix 0.38.21",
+ "rustix 0.38.26",
"signal-hook-registry",
"slab",
"windows-sys 0.48.0",
@@ -359,7 +371,7 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
dependencies = [
- "async-channel",
+ "async-channel 1.9.0",
"async-global-executor",
"async-io 1.13.0",
"async-lock 2.8.0",
@@ -550,9 +562,9 @@ dependencies = [
[[package]]
name = "bindgen"
-version = "0.68.1"
+version = "0.69.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
+checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2"
dependencies = [
"bitflags 2.4.1",
"cexpr",
@@ -616,16 +628,16 @@ dependencies = [
[[package]]
name = "blocking"
-version = "1.4.1"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a"
+checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
dependencies = [
- "async-channel",
- "async-lock 2.8.0",
+ "async-channel 2.1.1",
+ "async-lock 3.2.0",
"async-task",
"fastrand 2.0.1",
"futures-io",
- "futures-lite 1.13.0",
+ "futures-lite 2.1.0",
"piper",
"tracing",
]
@@ -815,9 +827,9 @@ dependencies = [
[[package]]
name = "concurrent-queue"
-version = "2.3.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400"
+checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
dependencies = [
"crossbeam-utils",
]
@@ -851,9 +863,9 @@ checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
[[package]]
name = "core-foundation"
-version = "0.9.3"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
@@ -861,9 +873,9 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "core-graphics"
@@ -893,9 +905,9 @@ dependencies = [
[[package]]
name = "core-graphics-types"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
@@ -915,11 +927,11 @@ dependencies = [
[[package]]
name = "coreaudio-sys"
-version = "0.2.13"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8478e5bdad14dce236b9898ea002eabfa87cbe14f0aa538dbe3b6a4bec4332d"
+checksum = "f3120ebb80a9de008e638ad833d4127d50ea3d3a960ea23ea69bc66d9358a028"
dependencies = [
- "bindgen 0.68.1",
+ "bindgen 0.69.1",
]
[[package]]
@@ -1215,9 +1227,9 @@ dependencies = [
[[package]]
name = "env_logger"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
dependencies = [
"humantime",
"is-terminal",
@@ -1234,12 +1246,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
-version = "0.3.6"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1250,9 +1262,20 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "event-listener"
-version = "3.0.1"
+version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01cec0252c2afff729ee6f00e903d479fba81784c8e2bd77447673471fdfaea1"
+checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae"
dependencies = [
"concurrent-queue",
"parking",
@@ -1261,11 +1284,11 @@ dependencies = [
[[package]]
name = "event-listener-strategy"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160"
+checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
dependencies = [
- "event-listener 3.0.1",
+ "event-listener 4.0.0",
"pin-project-lite",
]
@@ -1371,9 +1394,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
@@ -1399,6 +1422,21 @@ dependencies = [
"libc",
]
+[[package]]
+name = "futures"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
[[package]]
name = "futures-channel"
version = "0.3.29"
@@ -1406,6 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
+ "futures-sink",
]
[[package]]
@@ -1414,6 +1453,17 @@ version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+[[package]]
+name = "futures-executor"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
[[package]]
name = "futures-io"
version = "0.3.29"
@@ -1437,11 +1487,14 @@ dependencies = [
[[package]]
name = "futures-lite"
-version = "2.0.1"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb"
+checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143"
dependencies = [
+ "fastrand 2.0.1",
"futures-core",
+ "futures-io",
+ "parking",
"pin-project-lite",
]
@@ -1474,6 +1527,7 @@ version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
+ "futures-channel",
"futures-core",
"futures-io",
"futures-macro",
@@ -1552,9 +1606,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
-version = "0.14.2"
+version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@@ -1655,9 +1709,9 @@ dependencies = [
[[package]]
name = "idna"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@@ -1681,7 +1735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
- "hashbrown 0.14.2",
+ "hashbrown 0.14.3",
]
[[package]]
@@ -1740,7 +1794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi 0.3.3",
- "rustix 0.38.21",
+ "rustix 0.38.26",
"windows-sys 0.48.0",
]
@@ -1813,9 +1867,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.65"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -1923,9 +1977,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "linux-raw-sys"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lock_api"
@@ -2367,9 +2421,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
@@ -2459,16 +2513,16 @@ dependencies = [
[[package]]
name = "polling"
-version = "3.3.0"
+version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531"
+checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e"
dependencies = [
"cfg-if",
"concurrent-queue",
"pin-project-lite",
- "rustix 0.38.21",
+ "rustix 0.38.26",
"tracing",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2523,9 +2577,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@@ -2693,15 +2747,15 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.21"
+version = "0.38.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
dependencies = [
"bitflags 2.4.1",
"errno",
"libc",
- "linux-raw-sys 0.4.11",
- "windows-sys 0.48.0",
+ "linux-raw-sys 0.4.12",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2766,18 +2820,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
-version = "1.0.192"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.192"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@@ -2841,9 +2895,9 @@ dependencies = [
[[package]]
name = "shaderc"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31cef52787a0db5108788ea20bed13d6bf4b96287c5c5201e55725f7070f3443"
+checksum = "27e07913ada18607bb60d12431cbe3358d3bbebbe95948e1618851dc01e63b7b"
dependencies = [
"libc",
"shaderc-sys",
@@ -2851,9 +2905,9 @@ dependencies = [
[[package]]
name = "shaderc-sys"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e8f8439fffcffd6efcd74197204addf935dbab5752696bd990a6cd36d54cf64"
+checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7"
dependencies = [
"cmake",
"libc",
@@ -2898,18 +2952,18 @@ checksum = "a4f120bb98cb4cb0dab21c882968c3cbff79dd23b46f07b1cf5c25044945ce84"
[[package]]
name = "slotmap"
-version = "1.0.6"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [
"version_check",
]
[[package]]
name = "smallvec"
-version = "1.11.1"
+version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
+checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "smithay-client-toolkit"
@@ -3061,15 +3115,15 @@ dependencies = [
"cfg-if",
"fastrand 2.0.1",
"redox_syscall 0.4.1",
- "rustix 0.38.21",
+ "rustix 0.38.26",
"windows-sys 0.48.0",
]
[[package]]
name = "termcolor"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
+checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
@@ -3283,9 +3337,9 @@ checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]]
name = "url"
-version = "2.4.1"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
@@ -3431,9 +3485,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -3441,9 +3495,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
@@ -3456,9 +3510,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.38"
+version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02"
+checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
dependencies = [
"cfg-if",
"js-sys",
@@ -3468,9 +3522,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3478,9 +3532,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
@@ -3491,9 +3545,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "wayland-backend"
@@ -3656,9 +3710,9 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.65"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
+checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3673,7 +3727,7 @@ dependencies = [
"either",
"home",
"once_cell",
- "rustix 0.38.21",
+ "rustix 0.38.26",
]
[[package]]
@@ -3743,6 +3797,15 @@ dependencies = [
"windows-targets 0.48.5",
]
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
[[package]]
name = "windows-targets"
version = "0.42.2"
@@ -3773,6 +3836,21 @@ dependencies = [
"windows_x86_64_msvc 0.48.5",
]
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@@ -3785,6 +3863,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -3797,6 +3881,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -3809,6 +3899,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -3821,6 +3917,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -3833,6 +3935,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -3845,6 +3953,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -3857,6 +3971,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
[[package]]
name = "winit"
version = "0.28.7"
@@ -3894,9 +4014,9 @@ dependencies = [
[[package]]
name = "winnow"
-version = "0.5.19"
+version = "0.5.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b"
+checksum = "40460c13b1e9b107cfc9ede71232f204c36250bbf6f9c78515e2e27ead3122a7"
dependencies = [
"memchr",
]
@@ -3904,6 +4024,7 @@ dependencies = [
[[package]]
name = "wlx-capture"
version = "0.1.0"
+source = "git+https://github.com/galister/wlx-capture.git#56eba19fac9834e2dfb77dd3a0a93b088a93dee9"
dependencies = [
"ashpd",
"drm-fourcc",
@@ -3923,12 +4044,14 @@ dependencies = [
name = "wlx-overlay-s"
version = "0.1.0"
dependencies = [
+ "ash",
"ash-window",
"chrono",
"cstr",
- "env_logger 0.10.0",
+ "env_logger 0.10.1",
"fontconfig-rs",
"freetype-rs",
+ "futures",
"glam",
"idmap",
"idmap-derive",
@@ -4097,18 +4220,18 @@ dependencies = [
[[package]]
name = "zerocopy"
-version = "0.7.25"
+version = "0.7.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557"
+checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.25"
+version = "0.7.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
+checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index bf8c76e1..1aa792a3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,12 +6,14 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+ash = "^0.37.2"
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"
+futures = "0.3.29"
glam = { version = "0.24.1", features = ["approx"] }
idmap = "0.2.21"
idmap-derive = "0.1.2"
@@ -34,5 +36,5 @@ 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" }
+wlx-capture = { git = "https://github.com/galister/wlx-capture.git" }
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..7e94402e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# WlxOverlay-S
+[WIP] X11 & Wayland desktop overlay for SteamVR and OpenXR, Vulkan edition
+
+Nothing works yet.
diff --git a/src/backend/common.rs b/src/backend/common.rs
index 25bff554..b6e22645 100644
--- a/src/backend/common.rs
+++ b/src/backend/common.rs
@@ -1,152 +1,158 @@
-use std::{collections::VecDeque, time::Instant};
+use std::{
+ collections::{BinaryHeap, VecDeque},
+ sync::Arc,
+ time::Instant,
+};
-use glam::{Affine3A, Vec2, Vec3, Vec3A};
-use ovr_overlay::TrackedDeviceIndex;
+use idmap::IdMap;
-pub struct InputState {
- pub hmd: Affine3A,
- pub pointers: [Pointer; 2],
- pub devices: Vec,
- pub(super) data: TState,
+use crate::{
+ overlays::{
+ keyboard::create_keyboard,
+ screen::{get_screens_wayland, get_screens_x11},
+ watch::create_watch,
+ },
+ state::AppState,
+};
+
+use super::overlay::{OverlayData, OverlayState};
+
+pub struct OverlayContainer
+where
+ T: Default,
+{
+ overlays: IdMap>,
}
-impl InputState {
- pub fn pre_update(&mut self) {
- self.pointers[0].before = self.pointers[0].now;
- self.pointers[1].before = self.pointers[1].now;
+impl OverlayContainer
+where
+ T: Default,
+{
+ pub fn new(app: &mut AppState) -> Self {
+ let mut overlays = IdMap::new();
+
+ let screens = if std::env::var("WAYLAND_DISPLAY").is_ok() {
+ get_screens_wayland(&app.session)
+ } else {
+ get_screens_x11()
+ };
+
+ let watch = create_watch::(&app, &screens);
+ overlays.insert(watch.state.id, watch);
+
+ let keyboard = create_keyboard(&app);
+ overlays.insert(keyboard.state.id, keyboard);
+
+ let mut first = true;
+ for mut screen in screens {
+ if first {
+ screen.state.want_visible = true;
+ first = false;
+ }
+ overlays.insert(screen.state.id, screen);
+ }
+
+ Self { overlays }
}
- 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 fn mut_by_selector(&mut self, selector: &OverlaySelector) -> Option<&mut OverlayData> {
+ match selector {
+ OverlaySelector::Id(id) => self.mut_by_id(*id),
+ OverlaySelector::Name(name) => self.mut_by_name(name),
}
}
+
+ pub fn get_by_id<'a>(&'a mut self, id: usize) -> Option<&'a OverlayData> {
+ self.overlays.get(&id)
+ }
+
+ pub fn mut_by_id<'a>(&'a mut self, id: usize) -> Option<&'a mut OverlayData> {
+ self.overlays.get_mut(&id)
+ }
+
+ pub fn get_by_name<'a>(&'a mut self, name: &str) -> Option<&'a OverlayData> {
+ self.overlays.values().find(|o| *o.state.name == *name)
+ }
+
+ pub fn mut_by_name<'a>(&'a mut self, name: &str) -> Option<&'a mut OverlayData> {
+ self.overlays.values_mut().find(|o| *o.state.name == *name)
+ }
+
+ pub fn iter<'a>(&'a self) -> impl Iterator- > {
+ self.overlays.values()
+ }
+
+ pub fn iter_mut<'a>(&'a mut self) -> impl Iterator
- > {
+ self.overlays.values_mut()
+ }
}
-pub struct Pointer {
- pub hand: usize,
- pub pose: Affine3A,
- pub now: PointerState,
- pub before: PointerState,
- pub(super) interaction: InteractionState,
- pub(super) data: THand,
+pub enum OverlaySelector {
+ Id(usize),
+ Name(Arc),
}
-#[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,
+struct AppTask {
+ pub not_before: Instant,
+ pub task: TaskType,
}
-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 PartialEq for AppTask {
+ fn eq(&self, other: &Self) -> bool {
+ self.not_before == other.not_before
+ }
+}
+impl PartialOrd for AppTask {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ Some(self.not_before.cmp(&other.not_before).reverse())
+ }
+}
+impl Eq for AppTask {}
+impl Ord for AppTask {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.not_before.cmp(&other.not_before).reverse()
+ }
}
-impl Default for InteractionState {
- fn default() -> Self {
+pub enum TaskType {
+ Global(Box),
+ Overlay(
+ OverlaySelector,
+ Box,
+ ),
+}
+
+pub struct TaskContainer {
+ tasks: BinaryHeap,
+}
+
+impl TaskContainer {
+ pub fn new() -> Self {
Self {
- mode: PointerMode::Left,
- grabbed: None,
- clicked_id: None,
- hovered_id: None,
- release_actions: VecDeque::new(),
- next_push: Instant::now(),
+ tasks: BinaryHeap::new(),
+ }
+ }
+
+ pub fn enqueue(&mut self, task: TaskType) {
+ self.tasks.push(AppTask {
+ not_before: Instant::now(),
+ task,
+ });
+ }
+
+ pub fn enqueue_at(&mut self, task: TaskType, not_before: Instant) {
+ self.tasks.push(AppTask { not_before, task });
+ }
+
+ pub fn retrieve_due(&mut self, dest_buf: &mut VecDeque) {
+ let now = Instant::now();
+
+ while let Some(task) = self.tasks.peek() {
+ if task.not_before > now {
+ break;
+ }
+
+ dest_buf.push_back(self.tasks.pop().unwrap().task);
}
}
}
-
-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/input.rs b/src/backend/input.rs
new file mode 100644
index 00000000..625c7182
--- /dev/null
+++ b/src/backend/input.rs
@@ -0,0 +1,362 @@
+use std::{collections::VecDeque, time::Instant};
+
+use glam::{Affine3A, Vec2, Vec3A};
+use log::warn;
+use ovr_overlay::TrackedDeviceIndex;
+use tinyvec::array_vec;
+
+use crate::state::AppState;
+
+use super::{common::OverlayContainer, overlay::OverlayData};
+
+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,
+}
+
+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 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 Pointer {
+ pub idx: usize,
+ pub hand: u8,
+ 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,
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct PointerHit {
+ pub pointer: usize,
+ pub overlay: usize,
+ pub mode: PointerMode,
+ pub primary: bool,
+ pub uv: Vec2,
+ pub dist: f32,
+}
+
+pub trait InteractionHandler {
+ fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit);
+ fn on_left(&mut self, app: &mut AppState, pointer: usize);
+ fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
+ fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32);
+}
+
+pub struct DummyInteractionHandler;
+
+impl InteractionHandler for DummyInteractionHandler {
+ fn on_left(&mut self, _app: &mut AppState, _pointer: usize) {}
+ fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) {}
+ fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {}
+ fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {}
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+struct RayHit {
+ overlay: usize,
+ hit_pos: Vec3A,
+ dist: f32,
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct GrabData {
+ pub offset: Vec3A,
+ pub grabbed_id: usize,
+}
+
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, Default)]
+pub enum PointerMode {
+ #[default]
+ Left,
+ Right,
+ Middle,
+}
+
+impl Pointer {
+ pub fn interact(&mut self, overlays: &mut OverlayContainer, app: &mut AppState)
+ where
+ O: Default,
+ {
+ if let Some(grab_data) = self.interaction.grabbed {
+ if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) {
+ self.handle_grabbed(grabbed, grab_data.offset);
+ } else {
+ warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id);
+ self.interaction.grabbed = None;
+ }
+ return;
+ }
+
+ let Some(mut hit) = self.get_nearest_hit(overlays) else {
+ if let Some(hovered_id) = self.interaction.hovered_id.take() {
+ if let Some(hovered) = overlays.mut_by_id(hovered_id) {
+ hovered.backend.on_left(app, self.idx);
+ }
+ self.interaction.hovered_id = None;
+ }
+ if let Some(clicked_id) = self.interaction.clicked_id.take() {
+ if let Some(clicked) = overlays.mut_by_id(clicked_id) {
+ let hit = PointerHit {
+ pointer: self.idx,
+ overlay: clicked_id,
+ mode: self.interaction.mode,
+ ..Default::default()
+ };
+ clicked.backend.on_pointer(app, &hit, false);
+ }
+ }
+ return;
+ };
+
+ if let Some(hovered_id) = self.interaction.hovered_id {
+ if hovered_id != hit.overlay {
+ if let Some(old_hovered) = overlays.mut_by_id(hovered_id) {
+ if Some(self.idx) == old_hovered.primary_pointer {
+ old_hovered.primary_pointer = None;
+ }
+ old_hovered.backend.on_left(app, self.idx);
+ }
+ }
+ }
+ let Some(hovered) = overlays.mut_by_id(hit.overlay) else {
+ warn!("Hit overlay {} does not exist", hit.overlay);
+ return;
+ };
+
+ self.interaction.hovered_id = Some(hit.overlay);
+
+ if let Some(primary_pointer) = hovered.primary_pointer {
+ if hit.pointer < primary_pointer {
+ hovered.primary_pointer = Some(hit.pointer);
+ hit.primary = true;
+ }
+ } else {
+ hovered.primary_pointer = Some(hit.pointer);
+ hit.primary = true;
+ }
+ hovered.backend.on_hover(app, &hit);
+
+ if self.now.scroll.abs() > 0.1 {
+ hovered.backend.on_scroll(app, &hit, self.now.scroll);
+ }
+
+ if self.now.click && !self.before.click {
+ self.interaction.clicked_id = Some(hit.overlay);
+ hovered.backend.on_pointer(app, &hit, true);
+ } else if !self.now.click && self.before.click {
+ if let Some(clicked_id) = self.interaction.clicked_id.take() {
+ if let Some(clicked) = overlays.mut_by_id(clicked_id) {
+ clicked.backend.on_pointer(app, &hit, false);
+ }
+ } else {
+ hovered.backend.on_pointer(app, &hit, false);
+ }
+ }
+ }
+
+ fn get_nearest_hit(&mut self, overlays: &mut OverlayContainer) -> Option
+ where
+ O: Default,
+ {
+ let mut hits = array_vec!([RayHit; 8]);
+
+ for overlay in overlays.iter() {
+ if !overlay.state.want_visible {
+ continue;
+ }
+
+ if let Some(hit) = self.ray_test(overlay.state.id, &overlay.state.transform) {
+ hits.try_push(hit);
+ }
+ }
+
+ hits.sort_by(|a, b| a.dist.partial_cmp(&b.dist).unwrap());
+
+ for hit in hits.iter() {
+ let uv = overlays
+ .get_by_id(hit.overlay)
+ .unwrap() // this is safe
+ .state
+ .transform
+ .inverse()
+ .transform_point3a(hit.hit_pos)
+ .truncate();
+
+ if uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 {
+ continue;
+ }
+
+ return Some(PointerHit {
+ pointer: self.idx,
+ overlay: hit.overlay,
+ mode: self.interaction.mode,
+ primary: false,
+ uv,
+ dist: hit.dist,
+ });
+ }
+
+ None
+ }
+
+ fn start_grab(&mut self, overlay: &mut OverlayData)
+ where
+ O: Default,
+ {
+ let offset = self
+ .pose
+ .inverse()
+ .transform_point3a(overlay.state.transform.translation);
+
+ self.interaction.grabbed = Some(GrabData {
+ offset,
+ grabbed_id: overlay.state.id,
+ });
+ }
+ fn handle_grabbed(&mut self, overlay: &mut OverlayData, offset: Vec3A)
+ where
+ O: Default,
+ {
+ if self.now.grab {
+ overlay.state.transform.translation = self.pose.transform_point3a(offset);
+
+ if self.now.click && !self.before.click {
+ warn!("todo: click-while-grabbed");
+ }
+
+ match self.interaction.mode {
+ PointerMode::Left => {
+ overlay.state.transform.translation.y += self.now.scroll * 0.01;
+ }
+ _ => {
+ overlay.state.transform.matrix3 = overlay
+ .state
+ .transform
+ .matrix3
+ .mul_scalar(1.0 + 0.01 * self.now.scroll);
+ }
+ }
+ } else {
+ overlay.state.spawn_point = overlay.state.transform.translation;
+ self.interaction.grabbed = None;
+ }
+ }
+ fn ray_test(&self, overlay: usize, plane: &Affine3A) -> Option {
+ let plane_normal = plane.transform_vector3a(Vec3A::NEG_Z);
+ let ray_dir = self.pose.transform_vector3a(Vec3A::NEG_Z);
+
+ let d = plane.translation.dot(-plane_normal);
+ let dist = -(d + self.pose.translation.dot(plane_normal)) / ray_dir.dot(plane_normal);
+
+ let hit_pos = self.pose.translation + ray_dir * dist;
+
+ Some(RayHit {
+ overlay,
+ hit_pos,
+ dist,
+ })
+ }
+}
diff --git a/src/backend/mod.rs b/src/backend/mod.rs
index 55f52d26..01bfa9a2 100644
--- a/src/backend/mod.rs
+++ b/src/backend/mod.rs
@@ -1,3 +1,5 @@
pub mod common;
+pub mod input;
pub mod openvr;
pub mod openxr;
+pub mod overlay;
diff --git a/src/backend/openvr/input.rs b/src/backend/openvr/input.rs
index d9439433..5c5509b0 100644
--- a/src/backend/openvr/input.rs
+++ b/src/backend/openvr/input.rs
@@ -1,4 +1,4 @@
-use std::array;
+use std::{array, io::Write, path::Path};
use glam::Affine3A;
use ovr_overlay::{
@@ -11,7 +11,7 @@ use ovr_overlay::{
TrackedDeviceIndex,
};
-use crate::backend::common::{
+use crate::backend::input::{
InputState, InteractionState, Pointer, PointerState, TrackedDevice, TrackedDeviceRole,
};
@@ -96,7 +96,8 @@ impl InputState {
.collect::>()?;
let hands: [Pointer; 2] = array::from_fn(|i| Pointer:: {
- hand: i,
+ idx: i,
+ hand: i as u8,
now: PointerState::default(),
before: PointerState::default(),
pose: Affine3A::IDENTITY,
@@ -286,3 +287,30 @@ fn copy_from_hmd(in_mat: &HmdMatrix34_t, out_mat: &mut Affine3A) {
out_mat.w_axis[1] = in_mat.m[1][3];
out_mat.w_axis[2] = in_mat.m[2][3];
}
+
+pub fn action_manifest_path() -> &'static Path {
+ let action_path = "/tmp/wlxoverlay-s/actions.json";
+ std::fs::create_dir_all("/tmp/wlxoverlay-s").unwrap();
+
+ std::fs::File::create(action_path)
+ .unwrap()
+ .write_all(include_bytes!("../../res/actions.json"))
+ .unwrap();
+
+ std::fs::File::create("/tmp/wlxoverlay-s/actions_binding_knuckles.json")
+ .unwrap()
+ .write_all(include_bytes!("../../res/actions_binding_knuckles.json"))
+ .unwrap();
+
+ std::fs::File::create("/tmp/wlxoverlay-s/actions_binding_vive.json")
+ .unwrap()
+ .write_all(include_bytes!("../../res/actions_binding_vive.json"))
+ .unwrap();
+
+ std::fs::File::create("/tmp/wlxoverlay-s/actions_binding_oculus.json")
+ .unwrap()
+ .write_all(include_bytes!("../../res/actions_binding_oculus.json"))
+ .unwrap();
+
+ Path::new(action_path)
+}
diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs
index b2aa43d3..609b3491 100644
--- a/src/backend/openvr/mod.rs
+++ b/src/backend/openvr/mod.rs
@@ -1,4 +1,5 @@
use std::{
+ collections::VecDeque,
path::Path,
time::{Duration, Instant},
};
@@ -8,45 +9,73 @@ use ovr_overlay::{
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
TrackedDeviceIndex,
};
+use vulkano::{
+ device::{physical::PhysicalDevice, DeviceExtensions},
+ instance::InstanceExtensions,
+ Handle, VulkanObject,
+};
-use super::common::InputState;
+use crate::state::AppState;
+
+use self::{input::action_manifest_path, overlay::OpenVrOverlayData};
+
+use super::{
+ common::{OverlayContainer, TaskType},
+ input::InputState,
+};
pub mod input;
pub mod overlay;
-fn openvr_run() {
+pub 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 mut overlay_mngr = context.overlay_mngr();
+ //let mut settings_mngr = context.settings_mngr();
+ let mut input_mngr = context.input_mngr();
+ let mut system_mngr = context.system_mngr();
+ let mut compositor_mngr = context.compositor_mngr();
- let Ok(_) = input.set_action_manifest(Path::new("resources/actions.json")) else {
- error!("Failed to set action manifest");
+ let device_extensions_fn = |device: &PhysicalDevice| {
+ let names = compositor_mngr.get_vulkan_device_extensions_required(device.handle().as_raw());
+ let ext = DeviceExtensions::from_iter(names.iter().map(|s| s.as_str()));
+ ext
+ };
+
+ let mut compositor_mngr = context.compositor_mngr();
+ let instance_extensions = {
+ let names = compositor_mngr.get_vulkan_instance_extensions_required();
+ InstanceExtensions::from_iter(names.iter().map(|s| s.as_str()))
+ };
+
+ let mut state = AppState::new(instance_extensions, device_extensions_fn);
+ let mut overlays = OverlayContainer::::new(&mut state);
+
+ if let Err(e) = input_mngr.set_action_manifest(action_manifest_path()) {
+ error!("Failed to set action manifest: {}", e.description());
return;
};
- let Ok(mut input_state) = InputState::new(&mut input) else {
+ let Ok(mut input) = InputState::new(&mut input_mngr) else {
error!("Failed to initialize input");
return;
};
- let Ok(refresh_rate) = system.get_tracked_device_property::(TrackedDeviceIndex::HMD, ETrackedDeviceProperty::Prop_DisplayFrequency_Float) else {
+ let Ok(refresh_rate) = system_mngr.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();
+ let mut due_tasks = VecDeque::with_capacity(4);
loop {
- while let Some(event) = system.poll_next_event() {
+ while let Some(event) = system_mngr.poll_next_event() {
match event.event_type {
EVREventType::VREvent_Quit => {
info!("Received quit event, shutting down.");
@@ -61,35 +90,57 @@ fn openvr_run() {
}
if next_device_update <= Instant::now() {
- input_state.update_devices(&mut system);
+ input.update_devices(&mut system_mngr);
next_device_update = Instant::now() + Duration::from_secs(30);
}
- input_state.pre_update();
- input_state.update(&mut input, &mut system);
- input_state.post_update();
+ state.tasks.retrieve_due(&mut due_tasks);
+ while let Some(task) = due_tasks.pop_front() {
+ match task {
+ TaskType::Global(f) => f(&mut state),
+ TaskType::Overlay(sel, f) => {
+ if let Some(o) = overlays.mut_by_selector(&sel) {
+ f(&mut state, &mut o.state);
+ }
+ }
+ }
+ }
- // task scheduler
+ input.pre_update();
+ input.update(&mut input_mngr, &mut system_mngr);
+ input.post_update();
- // after input
+ input
+ .pointers
+ .iter_mut()
+ .for_each(|p| p.interact(&mut overlays, &mut state));
- // interactions
+ overlays
+ .iter_mut()
+ .for_each(|o| o.after_input(&mut overlay_mngr, &mut state));
- // show overlays
+ log::debug!("Rendering frame");
+
+ overlays
+ .iter_mut()
+ .filter(|o| o.state.want_visible)
+ .for_each(|o| o.render(&mut state));
+
+ log::debug!("Rendering overlays");
+
+ overlays
+ .iter_mut()
+ .for_each(|o| o.after_render(&mut overlay_mngr, &state.graphics));
// chaperone
- // render overlays
-
- // hide overlays
-
- // close font handles
+ // 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) {
+ if system_mngr.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
index 85ba9e4e..47ae59c5 100644
--- a/src/backend/openvr/overlay.rs
+++ b/src/backend/openvr/overlay.rs
@@ -1,15 +1,207 @@
-use ovr_overlay::sys::VRVulkanTextureData_t;
+use glam::Vec4;
+use ovr_overlay::{
+ overlay::{OverlayHandle, OverlayManager},
+ sys::VRVulkanTextureData_t,
+};
+use vulkano::{
+ command_buffer::{
+ synced::{
+ SyncCommandBuffer, SyncCommandBufferBuilder, SyncCommandBufferBuilderExecuteCommands,
+ },
+ AutoCommandBufferBuilder, CommandBufferExecFuture,
+ },
+ image::{ImageAccess, ImageLayout},
+ sync::{future::NowFuture, ImageMemoryBarrier},
+ Handle, VulkanObject,
+};
-use crate::overlays::OverlayData;
+use crate::{backend::overlay::OverlayData, graphics::WlxGraphics, state::AppState};
-pub(super) struct OpenVrOverlayManager {
- pub(super) overlays: Vec,
-}
-
-pub(super) struct OpenVrOverlay {
+#[derive(Default)]
+pub(super) struct OpenVrOverlayData {
+ handle: Option,
+ last_image: Option,
pub(super) visible: bool,
- pub(super) color: [f32; 4],
- overlay: OverlayData,
- handle: u32,
- ovr_texture: VRVulkanTextureData_t,
+ pub(super) color: Vec4,
+ pub(super) curvature: f32,
+ pub(super) sort_order: u32,
+}
+
+impl OverlayData {
+ pub fn initialize(
+ &mut self,
+ overlay: &mut OverlayManager,
+ app: &mut AppState,
+ ) -> OverlayHandle {
+ let key = format!("wlx-{}", self.state.name);
+ let handle = match overlay.create_overlay(&key, &key) {
+ Ok(handle) => handle,
+ Err(e) => {
+ panic!("Failed to create overlay: {}", e);
+ }
+ };
+ log::debug!("{}: initialize", self.state.name);
+
+ self.data.handle = Some(handle);
+
+ self.init(app);
+
+ self.upload_width(overlay);
+ self.upload_color(overlay);
+ self.upload_curvature(overlay);
+
+ handle
+ }
+
+ pub fn after_input(&mut self, overlay: &mut OverlayManager, app: &mut AppState) {
+ if self.state.want_visible && !self.data.visible {
+ self.show(overlay, app);
+ } else if !self.state.want_visible && self.data.visible {
+ self.hide(overlay);
+ }
+ }
+
+ pub fn after_render(&mut self, overlay: &mut OverlayManager, graphics: &WlxGraphics) {
+ if self.data.visible {
+ self.upload_texture(overlay, graphics);
+ }
+ }
+
+ fn show(&mut self, overlay: &mut OverlayManager, app: &mut AppState) {
+ let handle = match self.data.handle {
+ Some(handle) => handle,
+ None => self.initialize(overlay, app),
+ };
+ log::debug!("{}: show", self.state.name);
+ if let Err(e) = overlay.set_visibility(handle, true) {
+ panic!("Failed to show overlay: {}", e);
+ }
+ self.data.visible = true;
+ }
+
+ fn hide(&mut self, overlay: &mut OverlayManager) {
+ let Some(handle) = self.data.handle else {
+ return;
+ };
+ log::debug!("{}: hide", self.state.name);
+ if let Err(e) = overlay.set_visibility(handle, false) {
+ panic!("Failed to hide overlay: {}", e);
+ }
+ self.data.visible = false;
+ }
+
+ fn upload_color(&self, overlay: &mut OverlayManager) {
+ let Some(handle) = self.data.handle else {
+ log::debug!("{}: No overlay handle", self.state.name);
+ return;
+ };
+ if let Err(e) = overlay.set_opacity(handle, self.data.color.w) {
+ panic!("Failed to set overlay opacity: {}", e);
+ }
+ if let Err(e) = overlay.set_tint(
+ handle,
+ ovr_overlay::ColorTint {
+ r: self.data.color.x,
+ g: self.data.color.y,
+ b: self.data.color.z,
+ a: 1.0,
+ },
+ ) {
+ panic!("Failed to set overlay tint: {}", e);
+ }
+ }
+
+ fn upload_width(&self, overlay: &mut OverlayManager) {
+ let Some(handle) = self.data.handle else {
+ log::debug!("{}: No overlay handle", self.state.name);
+ return;
+ };
+ if let Err(e) = overlay.set_width(handle, self.state.width) {
+ panic!("Failed to set overlay width: {}", e);
+ }
+ }
+
+ fn upload_curvature(&self, overlay: &mut OverlayManager) {
+ let Some(handle) = self.data.handle else {
+ log::debug!("{}: No overlay handle", self.state.name);
+ return;
+ };
+ if let Err(e) = overlay.set_curvature(handle, self.data.curvature) {
+ panic!("Failed to set overlay curvature: {}", e);
+ }
+ }
+
+ fn upload_sort_order(&self, overlay: &mut OverlayManager) {
+ let Some(handle) = self.data.handle else {
+ log::debug!("{}: No overlay handle", self.state.name);
+ return;
+ };
+ if let Err(e) = overlay.set_sort_order(handle, self.data.sort_order) {
+ panic!("Failed to set overlay z order: {}", e);
+ }
+ }
+
+ fn upload_texture(&mut self, overlay: &mut OverlayManager, graphics: &WlxGraphics) {
+ let Some(handle) = self.data.handle else {
+ log::debug!("{}: No overlay handle", self.state.name);
+ return;
+ };
+
+ let Some(view) = self.backend.view() else {
+ log::debug!("{}: Not rendered", self.state.name);
+ return;
+ };
+
+ let image = view.image().inner().image.clone();
+
+ let raw_image = image.handle().as_raw();
+
+ if let Some(last_image) = self.data.last_image {
+ if last_image == raw_image {
+ return;
+ }
+ }
+
+ let Some(format) = image.format() else {
+ panic!("{}: Image format is None", self.state.name);
+ };
+
+ let dimensions = image.dimensions();
+
+ let mut texture = VRVulkanTextureData_t {
+ m_nImage: raw_image,
+ m_nFormat: format as _,
+ m_nWidth: dimensions.width(),
+ m_nHeight: dimensions.height(),
+ m_nSampleCount: image.samples() as u32,
+ m_pDevice: graphics.device.handle().as_raw() as *mut _,
+ m_pPhysicalDevice: graphics.device.physical_device().handle().as_raw() as *mut _,
+ m_pInstance: graphics.instance.handle().as_raw() as *mut _,
+ m_pQueue: graphics.queue.handle().as_raw() as *mut _,
+ m_nQueueFamilyIndex: graphics.queue.queue_family_index(),
+ };
+
+ graphics
+ .transition_layout(
+ image.clone(),
+ ImageLayout::ColorAttachmentOptimal,
+ ImageLayout::TransferSrcOptimal,
+ )
+ .wait(None)
+ .unwrap();
+
+ log::info!("nImage: {}, nFormat: {:?}, nWidth: {}, nHeight: {}, nSampleCount: {}, nQueueFamilyIndex: {}", texture.m_nImage, format, texture.m_nWidth, texture.m_nHeight, texture.m_nSampleCount, texture.m_nQueueFamilyIndex);
+ if let Err(e) = overlay.set_image_vulkan(handle, &mut texture) {
+ panic!("Failed to set overlay texture: {}", e);
+ }
+
+ graphics
+ .transition_layout(
+ image,
+ ImageLayout::TransferSrcOptimal,
+ ImageLayout::ColorAttachmentOptimal,
+ )
+ .wait(None)
+ .unwrap();
+ }
}
diff --git a/src/backend/overlay.rs b/src/backend/overlay.rs
new file mode 100644
index 00000000..d094e45e
--- /dev/null
+++ b/src/backend/overlay.rs
@@ -0,0 +1,170 @@
+use std::sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+};
+
+use glam::{Affine3A, Quat, Vec3A};
+use vulkano::image::ImageViewAbstract;
+
+use crate::state::AppState;
+
+use super::input::{DummyInteractionHandler, InteractionHandler, PointerHit};
+
+static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
+
+pub trait OverlayBackend: OverlayRenderer + InteractionHandler {}
+
+pub struct OverlayState {
+ 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: Vec3A,
+ pub spawn_rotation: Quat,
+ pub relative_to: RelativeTo,
+ pub primary_pointer: Option,
+ pub interaction_transform: Affine3A,
+}
+
+impl Default for OverlayState {
+ fn default() -> Self {
+ OverlayState {
+ 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: Vec3A::NEG_Z,
+ spawn_rotation: Quat::IDENTITY,
+ transform: Affine3A::IDENTITY,
+ primary_pointer: None,
+ interaction_transform: Affine3A::IDENTITY,
+ }
+ }
+}
+
+pub struct OverlayData
+where
+ T: Default,
+{
+ pub state: OverlayState,
+ pub backend: Box,
+ pub primary_pointer: Option,
+ pub data: T,
+}
+
+impl Default for OverlayData
+where
+ T: Default,
+{
+ fn default() -> Self {
+ OverlayData {
+ state: Default::default(),
+ backend: Box::new(SplitOverlayBackend::default()),
+ primary_pointer: None,
+ data: Default::default(),
+ }
+ }
+}
+
+impl OverlayState {
+ pub fn reset(&mut self, _app: &mut AppState) {
+ todo!()
+ }
+}
+
+impl OverlayData
+where
+ T: Default,
+{
+ 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) -> Option> {
+ 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) -> Option>;
+}
+
+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) -> Option> {
+ unimplemented!()
+ }
+}
+// Boilerplate and dummies
+
+pub enum RelativeTo {
+ None,
+ Head,
+ Hand(usize),
+}
+
+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) -> Option> {
+ self.renderer.view()
+ }
+}
+impl InteractionHandler for SplitOverlayBackend {
+ fn on_left(&mut self, app: &mut AppState, pointer: usize) {
+ self.interaction.on_left(app, pointer);
+ }
+ fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) {
+ 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/graphics.rs b/src/graphics.rs
index 725b3a3f..5ce061a4 100644
--- a/src/graphics.rs
+++ b/src/graphics.rs
@@ -1,10 +1,67 @@
-use std::{sync::Arc, slice::Iter, io::Cursor, error::Error};
+use std::{error::Error, io::Cursor, slice::Iter, sync::Arc};
-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 ash::vk::SubmitInfo;
+use log::{debug, error, info};
+use smallvec::smallvec;
+use vulkano::{
+ buffer::{
+ allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
+ Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
+ },
+ command_buffer::{
+ allocator::{
+ CommandBufferAllocator, CommandBufferBuilderAlloc, StandardCommandBufferAllocator,
+ },
+ sys::{CommandBufferBeginInfo, UnsafeCommandBufferBuilder},
+ AutoCommandBufferBuilder, CommandBufferExecFuture, CommandBufferInheritanceInfo,
+ CommandBufferInheritanceRenderPassType, CommandBufferInheritanceRenderingInfo,
+ CommandBufferLevel, CommandBufferUsage, PrimaryAutoCommandBuffer,
+ PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo,
+ SecondaryAutoCommandBuffer, SubpassContents,
+ },
+ descriptor_set::{
+ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
+ },
+ device::{
+ physical::{PhysicalDevice, PhysicalDeviceType},
+ Device, DeviceCreateInfo, DeviceExtensions, Features, Queue, QueueCreateInfo, QueueFlags,
+ },
+ format::Format,
+ image::{
+ sys::Image, AttachmentImage, ImageAccess, ImageCreateFlags, ImageDimensions, ImageError,
+ ImageLayout, ImageUsage, ImageViewAbstract, ImmutableImage, MipmapsCount, StorageImage,
+ SubresourceData, SwapchainImage,
+ },
+ instance::{Instance, InstanceCreateInfo, InstanceExtensions},
+ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
+ pipeline::{
+ graphics::{
+ color_blend::{AttachmentBlend, ColorBlendState},
+ input_assembly::InputAssemblyState,
+ render_pass::PipelineRenderingCreateInfo,
+ vertex_input::Vertex,
+ viewport::{Viewport, ViewportState},
+ },
+ GraphicsPipeline, Pipeline, PipelineBindPoint,
+ },
+ render_pass::{LoadOp, StoreOp},
+ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
+ shader::ShaderModule,
+ swapchain::{CompositeAlpha, Surface, Swapchain, SwapchainCreateInfo},
+ sync::{
+ fence::Fence, future::NowFuture, AccessFlags, DependencyInfo, ImageMemoryBarrier,
+ PipelineStages,
+ },
+ Version, VulkanLibrary, VulkanObject,
+};
use vulkano_win::VkSurfaceBuild;
-use wlx_capture::frame::{DmabufFrame, DRM_FORMAT_ABGR8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888};
+use winit::{
+ event_loop::EventLoop,
+ window::{Window, WindowBuilder},
+};
+use wlx_capture::frame::{
+ DmabufFrame, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888,
+};
#[repr(C)]
#[derive(BufferContents, Vertex, Copy, Clone, Debug)]
@@ -15,10 +72,10 @@ pub struct Vert2Uv {
pub in_uv: [f32; 2],
}
-pub const INDICES : [u16; 6] = [2, 1, 0, 1, 2, 3];
+pub const INDICES: [u16; 6] = [2, 1, 0, 1, 2, 3];
pub struct WlxGraphics {
- pub instance: Arc,
+ pub instance: Arc,
pub device: Arc,
pub queue: Arc,
@@ -28,18 +85,26 @@ pub struct WlxGraphics {
pub command_buffer_allocator: Arc,
pub descriptor_set_allocator: Arc,
+ pub quad_verts: Subbuffer<[Vert2Uv]>,
pub quad_indices: Subbuffer<[u16]>,
}
impl WlxGraphics {
- pub fn new() -> (Arc, EventLoop<()>) {
+ pub fn new(
+ vk_instance_extensions: InstanceExtensions,
+ mut vk_device_extensions_fn: impl FnMut(&PhysicalDevice) -> DeviceExtensions,
+ ) -> (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 library_extensions = vulkano_win::required_extensions(&library);
+ let required_extensions = library_extensions.union(&vk_instance_extensions);
+
+ debug!("Instance exts for app: {:?}", &required_extensions);
+ debug!("Instance exts for runtime: {:?}", &vk_instance_extensions);
let instance = Instance::new(
library,
@@ -61,13 +126,14 @@ impl WlxGraphics {
..DeviceExtensions::empty()
};
+ debug!("Device exts for app: {:?}", &device_extensions);
+
// 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()
@@ -75,7 +141,14 @@ impl WlxGraphics {
p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering
})
.filter(|p| {
- p.supported_extensions().contains(&device_extensions)
+ let runtime_extensions = vk_device_extensions_fn(p);
+ debug!(
+ "Device exts for {}: {:?}",
+ p.properties().device_name,
+ &runtime_extensions
+ );
+ let my_extensions = runtime_extensions.union(&device_extensions);
+ p.supported_extensions().contains(&my_extensions)
})
.filter_map(|p| {
p.queue_family_properties()
@@ -87,20 +160,18 @@ impl WlxGraphics {
})
.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,
- }
+ .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.",
+ "Using vkPhysicalDevice: {}",
physical_device.properties().device_name,
);
@@ -128,8 +199,44 @@ impl WlxGraphics {
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 command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
+ device.clone(),
+ Default::default(),
+ ));
+ let descriptor_set_allocator =
+ Arc::new(StandardDescriptorSetAllocator::new(device.clone()));
+
+ let vertices = [
+ Vert2Uv {
+ in_pos: [0., 0.],
+ in_uv: [0., 0.],
+ },
+ Vert2Uv {
+ in_pos: [0., 1.],
+ in_uv: [0., 1.],
+ },
+ Vert2Uv {
+ in_pos: [1., 0.],
+ in_uv: [1., 0.],
+ },
+ Vert2Uv {
+ in_pos: [1., 1.],
+ in_uv: [1., 1.],
+ },
+ ];
+ let quad_verts = Buffer::from_iter(
+ &memory_allocator,
+ BufferCreateInfo {
+ usage: BufferUsage::VERTEX_BUFFER,
+ ..Default::default()
+ },
+ AllocationCreateInfo {
+ usage: MemoryUsage::Upload,
+ ..Default::default()
+ },
+ vertices.into_iter(),
+ )
+ .unwrap();
let quad_indices = Buffer::from_iter(
&memory_allocator,
@@ -142,7 +249,8 @@ impl WlxGraphics {
..Default::default()
},
INDICES.iter().cloned(),
- ).unwrap();
+ )
+ .unwrap();
let me = Self {
instance,
@@ -153,21 +261,30 @@ impl WlxGraphics {
command_buffer_allocator,
descriptor_set_allocator,
quad_indices,
+ quad_verts,
};
(Arc::new(me), event_loop)
}
- pub fn create_swapchain(&self, format: Option) -> (Arc, Vec>) {
+ 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
+ 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 composite_alpha = surface_capabilities
+ .supported_composite_alpha
+ .into_iter()
+ .next()
+ .unwrap();
let image_format = Some(
self.device
@@ -176,9 +293,18 @@ impl WlxGraphics {
.unwrap()[0]
.0,
);
- (surface_capabilities.min_image_count, composite_alpha, image_format.unwrap())
+ (
+ surface_capabilities.min_image_count,
+ composite_alpha,
+ image_format.unwrap(),
+ )
};
- let window = self.surface.object().unwrap().downcast_ref::().unwrap();
+ let window = self
+ .surface
+ .object()
+ .unwrap()
+ .downcast_ref::()
+ .unwrap();
let swapchain = Swapchain::new(
self.device.clone(),
self.surface.clone(),
@@ -196,7 +322,15 @@ impl WlxGraphics {
swapchain
}
- pub fn upload_verts(&self, width: f32, height: f32, x: f32, y: f32, w: f32, h: f32) -> Subbuffer<[Vert2Uv]> {
+ 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;
@@ -207,16 +341,30 @@ impl WlxGraphics {
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] },
+ 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 {
+ where
+ T: BufferContents + Clone,
+ {
Buffer::from_iter(
&self.memory_allocator,
BufferCreateInfo {
@@ -228,8 +376,9 @@ impl WlxGraphics {
..Default::default()
},
contents.cloned(),
- ).unwrap()
- }
+ )
+ .unwrap()
+ }
pub fn dmabuf_texture(&self, frame: DmabufFrame) -> Result, ImageError> {
let dimensions = ImageDimensions::Dim2d {
@@ -246,7 +395,8 @@ impl WlxGraphics {
_ => panic!("Unsupported dmabuf format {:x}", frame.format.fourcc),
};
- let planes = frame.planes
+ let planes = frame
+ .planes
.iter()
.take(frame.num_planes)
.filter_map(|plane| {
@@ -258,7 +408,8 @@ impl WlxGraphics {
offset: plane.offset as _,
row_pitch: plane.stride as _,
})
- }).collect();
+ })
+ .collect();
StorageImage::new_from_dma_buf_fd(
&self.memory_allocator,
@@ -272,28 +423,112 @@ impl WlxGraphics {
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],
+ &self.memory_allocator,
+ [width, height],
format,
ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC | ImageUsage::COLOR_ATTACHMENT,
- ).unwrap();
+ )
+ .unwrap();
tex
}
- pub fn create_pipeline(self: &Arc, vert: Arc, frag: Arc, format: Format) -> Arc {
+
+ 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 {
+
+ 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 }
+ )
+ .unwrap();
+ WlxCommandBuffer {
+ graphics: self.clone(),
+ command_buffer,
+ }
}
+ pub fn transition_layout(
+ &self,
+ image: Arc,
+ old_layout: ImageLayout,
+ new_layout: ImageLayout,
+ ) -> Fence {
+ let barrier = ImageMemoryBarrier {
+ src_stages: PipelineStages::ALL_TRANSFER,
+ src_access: AccessFlags::TRANSFER_WRITE,
+ dst_stages: PipelineStages::ALL_TRANSFER,
+ dst_access: AccessFlags::TRANSFER_READ,
+ old_layout,
+ new_layout,
+ subresource_range: image.subresource_range(),
+ ..ImageMemoryBarrier::image(image)
+ };
+
+ let builder_alloc = self
+ .command_buffer_allocator
+ .allocate(
+ self.queue.queue_family_index(),
+ CommandBufferLevel::Primary,
+ 1,
+ )
+ .unwrap()
+ .next()
+ .unwrap();
+
+ let command_buffer = unsafe {
+ let mut builder = UnsafeCommandBufferBuilder::new(
+ &builder_alloc.inner(),
+ CommandBufferBeginInfo {
+ usage: CommandBufferUsage::OneTimeSubmit,
+ ..Default::default()
+ },
+ )
+ .unwrap();
+
+ builder.pipeline_barrier(&DependencyInfo {
+ image_memory_barriers: smallvec![barrier],
+ ..Default::default()
+ });
+ builder.build().unwrap()
+ };
+
+ let fence = vulkano::sync::fence::Fence::new(
+ self.device.clone(),
+ vulkano::sync::fence::FenceCreateInfo::default(),
+ )
+ .unwrap();
+
+ let fns = self.device.fns();
+ unsafe {
+ (fns.v1_0.queue_submit)(
+ self.queue.handle(),
+ 1,
+ [SubmitInfo::builder()
+ .command_buffers(&[command_buffer.handle()])
+ .build()]
+ .as_ptr(),
+ fence.handle(),
+ )
+ }
+ .result()
+ .unwrap();
+
+ fence
+ }
}
pub struct WlxCommandBuffer {
@@ -306,7 +541,9 @@ impl WlxCommandBuffer {
&self.command_buffer
}
- pub fn inner_mut(&mut self) -> &mut AutoCommandBufferBuilder> {
+ pub fn inner_mut(
+ &mut self,
+ ) -> &mut AutoCommandBufferBuilder> {
&mut self.command_buffer
}
@@ -314,37 +551,55 @@ impl WlxCommandBuffer {
self.command_buffer
}
- pub fn begin(mut self, render_target: Arc) -> Self
- {
+ pub fn begin(
+ mut self,
+ render_target: Arc,
+ want_layout: Option,
+ ) -> Self {
+ if let Some(want_layout) = want_layout {
+ let mut barrier =
+ ImageMemoryBarrier::image(render_target.image().inner().image.clone());
+ barrier.old_layout = ImageLayout::ColorAttachmentOptimal;
+ barrier.new_layout = want_layout;
+ }
+
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();
+ .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();
+ 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());
+ 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 {
+ pub fn texture2d(
+ &mut self,
+ width: u32,
+ height: u32,
+ format: Format,
+ data: Vec,
+ ) -> Arc {
let dimensions = ImageDimensions::Dim2d {
width,
height,
@@ -374,7 +629,6 @@ impl WlxCommandBuffer {
reader.next_frame(&mut image_data).unwrap();
self.texture2d(width, height, Format::R8G8B8A8_UNORM, image_data)
}
-
}
impl WlxCommandBuffer {
@@ -414,26 +668,33 @@ pub struct WlxPipeline {
}
impl WlxPipeline {
- fn new(graphics: Arc, vert: Arc, frag: Arc, format: Format) -> Self {
+ 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();
+ .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}
+ Self {
+ graphics,
+ pipeline,
+ format,
+ }
}
pub fn inner(&self) -> Arc {
@@ -444,7 +705,12 @@ impl WlxPipeline {
self.graphics.clone()
}
- pub fn uniform_sampler(&self, set: usize, texture: Arc, filter: Filter) -> Arc {
+ pub fn uniform_sampler(
+ &self,
+ set: usize,
+ texture: Arc,
+ filter: Filter,
+ ) -> Arc {
let sampler = Sampler::new(
self.graphics.device.clone(),
SamplerCreateInfo {
@@ -455,7 +721,7 @@ impl WlxPipeline {
},
)
.unwrap();
-
+
let layout = self.pipeline.layout().set_layouts().get(set).unwrap();
PersistentDescriptorSet::new(
@@ -467,7 +733,9 @@ impl WlxPipeline {
}
pub fn uniform_buffer(&self, set: usize, data: Vec) -> Arc
- where T: BufferContents + Copy {
+ where
+ T: BufferContents + Copy,
+ {
let uniform_buffer = SubbufferAllocator::new(
self.graphics.memory_allocator.clone(),
SubbufferAllocatorCreateInfo {
@@ -486,27 +754,44 @@ impl WlxPipeline {
PersistentDescriptorSet::new(
&self.graphics.descriptor_set_allocator,
layout.clone(),
- [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)]
- ).unwrap()
+ [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 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
-{
+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 {
+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,
@@ -515,21 +800,23 @@ impl WlxPass
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();
+ &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])
+ command_buffer
+ .set_viewport(0, [viewport])
.bind_pipeline_graphics(pipeline_inner)
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
@@ -545,9 +832,10 @@ impl WlxPass
error!("Failed to draw: {}", source);
}
Err(err)
- }).unwrap();
+ })
+ .unwrap();
- Self {
+ Self {
pipeline,
vertex_buffer,
index_buffer,
@@ -556,4 +844,3 @@ impl WlxPass
}
}
}
-
diff --git a/src/gui/font.rs b/src/gui/font.rs
index 19641b5e..15b48d73 100644
--- a/src/gui/font.rs
+++ b/src/gui/font.rs
@@ -4,7 +4,7 @@ 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 vulkano::{command_buffer::CommandBufferUsage, format::Format, image::ImmutableImage};
use crate::graphics::WlxGraphics;
@@ -23,9 +23,6 @@ struct FontCollection {
struct Font {
face: Face,
- path: String,
- index: isize,
- size: isize,
glyphs: IdMap>,
}
@@ -50,7 +47,12 @@ impl FontCache {
}
}
- pub fn get_text_size(&mut self, text: &str, size: isize, graphics: Arc) -> (f32, f32) {
+ 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);
@@ -59,7 +61,10 @@ impl FontCache {
for line in text.lines() {
let w: f32 = line
.chars()
- .map(|c| self.get_glyph_for_cp(c as usize, size, graphics.clone()).advance)
+ .map(|c| {
+ self.get_glyph_for_cp(c as usize, size, graphics.clone())
+ .advance
+ })
.sum();
if w > max_w {
@@ -69,7 +74,12 @@ impl FontCache {
(max_w, height)
}
- pub fn get_glyphs(&mut self, text: &str, size: isize, graphics: Arc) -> Vec> {
+ 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() {
@@ -143,13 +153,7 @@ impl FontCache {
let mut glyphs = IdMap::new();
glyphs.insert(0, zero_glyph);
- let font = Font {
- face,
- path: path.to_string(),
- size,
- index: font_idx as _,
- glyphs,
- };
+ let font = Font { face, glyphs };
coll.fonts.push(font);
idx
@@ -159,7 +163,12 @@ impl FontCache {
}
}
- fn get_glyph_for_cp(&mut self, cp: usize, size: isize, graphics: Arc) -> Rc {
+ 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];
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
index cfb9734f..111ca1fd 100644
--- a/src/gui/mod.rs
+++ b/src/gui/mod.rs
@@ -4,16 +4,16 @@ use glam::{Vec2, Vec3};
use vulkano::{
command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer},
format::Format,
- image::{view::ImageView, AttachmentImage},
+ image::{view::ImageView, AttachmentImage, ImageLayout, ImageViewAbstract},
sampler::Filter,
};
use crate::{
- graphics::{WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline},
- overlays::{
- interactions::{InteractionHandler, PointerHit},
- OverlayBackend, OverlayRenderer,
+ backend::{
+ input::{InteractionHandler, PointerHit},
+ overlay::{OverlayBackend, OverlayRenderer},
},
+ graphics::{WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline},
shaders::{frag_color, frag_glyph, frag_sprite, vert_common},
state::AppState,
};
@@ -203,11 +203,8 @@ pub struct Canvas {
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,
@@ -284,11 +281,8 @@ impl Canvas {
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,
@@ -323,7 +317,7 @@ impl Canvas {
.canvas
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
- .begin(self.view_bg.clone());
+ .begin(self.view_bg.clone(), None);
for c in self.controls.iter_mut() {
if let Some(fun) = c.on_render_bg {
fun(c, &self.canvas, app, &mut cmd_buffer);
@@ -337,7 +331,7 @@ impl Canvas {
.canvas
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
- .begin(self.view_fg.clone());
+ .begin(self.view_fg.clone(), None);
for c in self.controls.iter_mut() {
if let Some(fun) = c.on_render_fg {
fun(c, &self.canvas, app, &mut cmd_buffer);
@@ -352,32 +346,32 @@ impl Canvas {
}
impl InteractionHandler for Canvas {
- fn on_left(&mut self, _app: &mut AppState, hand: usize) {
- self.hover_controls[hand] = None;
+ fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
+ self.hover_controls[pointer] = 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);
+ self.hover_controls[hit.pointer] = Some(i);
} else {
- self.hover_controls[hit.hand] = None;
+ self.hover_controls[hit.pointer] = 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]
+ self.pressed_controls[hit.pointer]
};
if let Some(idx) = idx {
let c = &mut self.controls[idx];
if pressed {
if let Some(ref mut f) = c.on_press {
- self.pressed_controls[hit.hand] = Some(idx);
+ self.pressed_controls[hit.pointer] = 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;
+ self.pressed_controls[hit.pointer] = None;
f(c, &mut self.canvas.data, app);
}
}
@@ -410,7 +404,10 @@ impl OverlayRenderer for Canvas {
.canvas
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
- .begin(self.view_final.clone());
+ .begin(
+ self.view_final.clone(),
+ Some(ImageLayout::TransferSrcOptimal),
+ );
if dirty {
self.render_fg(app);
@@ -437,8 +434,8 @@ impl OverlayRenderer for Canvas {
let _ = cmd_buffer.end_render_and_execute();
}
- fn view(&mut self) -> Arc {
- self.view_final.clone()
+ fn view(&mut self) -> Option> {
+ Some(self.view_final.clone())
}
}
diff --git a/src/main.rs b/src/main.rs
index fac077a6..1ec3ec54 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,43 +4,12 @@ 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 crate::backend::openvr::openvr_run;
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};
+use log::info;
fn main() {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
@@ -50,222 +19,5 @@ fn main() {
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::>()
+ openvr_run();
}
diff --git a/src/overlays/interactions.rs b/src/overlays/interactions.rs
deleted file mode 100644
index 6f052269..00000000
--- a/src/overlays/interactions.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-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
index 63540ac6..b7e0ce17 100644
--- a/src/overlays/keyboard.rs
+++ b/src/overlays/keyboard.rs
@@ -10,23 +10,25 @@ use std::{
};
use crate::{
+ backend::overlay::{OverlayData, OverlayState},
gui::{color_parse, CanvasBuilder, Control},
input::{KeyModifier, VirtualKey, KEYS_TO_MODS},
state::AppState,
};
-use glam::{vec2, vec3};
+use glam::{vec2, vec3a};
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 {
+pub fn create_keyboard(app: &AppState) -> OverlayData
+where
+ O: Default,
+{
let size = vec2(
LAYOUT.row_size * PIXELS_PER_UNIT,
(LAYOUT.main_layout.len() as f32) * PIXELS_PER_UNIT,
@@ -106,12 +108,15 @@ pub fn create_keyboard(app: &AppState) -> OverlayData {
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.),
+ state: OverlayState {
+ name: Arc::from("Kbd"),
+ show_hide: true,
+ width: LAYOUT.row_size * 0.05,
+ size: (size.x as _, size.y as _),
+ grabbable: true,
+ spawn_point: vec3a(0., -0.5, -1.),
+ ..Default::default()
+ },
backend: Box::new(canvas),
..Default::default()
}
diff --git a/src/overlays/mod.rs b/src/overlays/mod.rs
index a49a8a9c..01e06979 100644
--- a/src/overlays/mod.rs
+++ b/src/overlays/mod.rs
@@ -1,145 +1,3 @@
-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 screen;
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
index 72566077..9de27c68 100644
--- a/src/overlays/screen.rs
+++ b/src/overlays/screen.rs
@@ -1,20 +1,59 @@
+use log::{info, warn};
+use std::{
+ f32::consts::PI,
+ path::Path,
+ sync::{mpsc::Receiver, Arc},
+ time::{Duration, Instant},
+};
+use vulkano::{
+ command_buffer::CommandBufferUsage,
+ format::Format,
+ image::{view::ImageView, ImageAccess, ImageViewAbstract, ImmutableImage},
+ Handle, VulkanObject,
+};
+use wlx_capture::{
+ frame::WlxFrame,
+ pipewire::{pipewire_select_screen, PipewireCapture},
+ wayland::{Transform, WlxClient, WlxOutput},
+ wlr::WlrDmabufCapture,
+ WlxCapture,
+};
-pub struct ScreenInteractionData {
+use glam::{vec2, Affine2, Quat, Vec2, Vec3};
+
+use crate::{
+ backend::{
+ input::{InteractionHandler, PointerHit, PointerMode},
+ overlay::{OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend},
+ },
+ input::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT},
+ state::{AppSession, AppState},
+};
+
+pub struct ScreenInteractionHandler {
next_scroll: Instant,
next_move: Instant,
mouse_transform: Affine2,
}
-impl ScreenInteractionData {
+impl ScreenInteractionHandler {
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),
+ 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 {
@@ -25,7 +64,222 @@ impl ScreenInteractionData {
}
}
-struct ScreenInteractionHandler {
+impl InteractionHandler for ScreenInteractionHandler {
+ fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) {
+ if self.next_move < Instant::now() {
+ let pos = self.mouse_transform.transform_point2(hit.uv);
+ app.input.mouse_move(pos);
+ }
+ }
+ fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
+ let pos = self.mouse_transform.transform_point2(hit.uv);
+ app.input.mouse_move(pos);
+ let btn = match hit.mode {
+ PointerMode::Right => MOUSE_RIGHT,
+ PointerMode::Middle => MOUSE_MIDDLE,
+ _ => MOUSE_LEFT,
+ };
+
+ if pressed {
+ self.next_move = Instant::now() + Duration::from_millis(300);
+ }
+
+ app.input.send_button(btn, pressed);
+ }
+ fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta: f32) {
+ let millis = (1. - delta.abs()) * delta;
+ if let Some(next_scroll) = Instant::now().checked_add(Duration::from_millis(millis as _)) {
+ self.next_scroll = next_scroll;
+ }
+ app.input.wheel(if delta < 0. { -1 } else { 1 })
+ }
+ fn on_left(&mut self, _app: &mut AppState, _hand: usize) {}
}
+pub struct ScreenRenderer {
+ capture: Box,
+ resolution: (i32, i32),
+ receiver: Option>,
+ view: Option>,
+}
+
+impl ScreenRenderer {
+ pub fn new_wlr(output: &WlxOutput) -> Option {
+ let Some(client) = WlxClient::new() else {
+ return None;
+ };
+ let Some(capture) = WlrDmabufCapture::new(client, output.id) else {
+ return None;
+ };
+ Some(ScreenRenderer {
+ capture: Box::new(capture),
+ resolution: output.size,
+ receiver: None,
+ view: None,
+ })
+ }
+
+ pub fn new_pw(
+ output: &WlxOutput,
+ token: Option<&str>,
+ _fallback: bool,
+ ) -> Option {
+ let name = output.name.clone();
+ let node_id = futures::executor::block_on(pipewire_select_screen(token)).ok()?;
+
+ let capture = PipewireCapture::new(name, node_id, 60);
+
+ Some(ScreenRenderer {
+ capture: Box::new(capture),
+ resolution: output.size,
+ receiver: None,
+ view: None,
+ })
+ }
+
+ pub fn new_xshm() -> ScreenRenderer {
+ todo!()
+ }
+}
+
+impl OverlayRenderer for ScreenRenderer {
+ fn init(&mut self, app: &mut AppState) {
+ self.receiver = Some(self.capture.init());
+ let mut cmd = app
+ .graphics
+ .create_command_buffer(CommandBufferUsage::OneTimeSubmit);
+ let default_image = cmd.texture2d(1, 1, Format::R8G8B8A8_UNORM, vec![255, 0, 255, 255]);
+ let _ = cmd.end_and_execute();
+ }
+ fn render(&mut self, app: &mut AppState) {
+ let Some(receiver) = self.receiver.as_mut() else {
+ log::error!("No receiver");
+ return;
+ };
+
+ for frame in receiver.try_iter() {
+ match frame {
+ WlxFrame::Dmabuf(frame) => {
+ if let Ok(new) = app.graphics.dmabuf_texture(frame) {
+ if let Some(current) = self.view.as_ref() {
+ if current.image().inner().image.handle().as_raw()
+ == new.inner().image.handle().as_raw()
+ {
+ return;
+ }
+ }
+ self.view = Some(ImageView::new_default(new).unwrap());
+ }
+ }
+ WlxFrame::MemFd(frame) => {
+ todo!()
+ }
+ WlxFrame::MemPtr(frame) => {
+ todo!()
+ }
+ _ => {}
+ };
+ }
+ self.capture.request_new_frame();
+ }
+ fn pause(&mut self, _app: &mut AppState) {
+ self.capture.pause();
+ }
+ fn resume(&mut self, _app: &mut AppState) {
+ self.capture.resume();
+ }
+ fn view(&mut self) -> Option> {
+ self.view.as_ref().and_then(|v| Some(v.clone()))
+ }
+}
+
+fn try_create_screen(wl: &WlxClient, idx: usize, session: &AppSession) -> Option>
+where
+ O: Default,
+{
+ let output = &wl.outputs[idx];
+ info!(
+ "{}: Res {}x{} Size {:?} Pos {:?}",
+ output.name, output.size.0, output.size.1, output.logical_size, output.logical_pos,
+ );
+
+ let size = (output.size.0, output.size.1);
+ let mut capture: Option = None;
+
+ if session.capture_method == "auto" && wl.maybe_wlr_dmabuf_mgr.is_some() {
+ info!("{}: Using Wlr DMA-Buf", &output.name);
+ capture = ScreenRenderer::new_wlr(output);
+ }
+
+ if capture.is_none() {
+ info!("{}: Using Pipewire capture", &output.name);
+ let file_name = format!("{}.token", &output.name);
+ let full_path = Path::new(&session.config_path).join(file_name);
+ let token = std::fs::read_to_string(full_path).ok();
+
+ capture = ScreenRenderer::new_pw(
+ output,
+ token.as_deref(),
+ session.capture_method == "pw_fallback",
+ );
+ }
+ if let Some(capture) = capture {
+ let backend = Box::new(SplitOverlayBackend {
+ renderer: Box::new(capture),
+ interaction: Box::new(ScreenInteractionHandler::new(
+ vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32),
+ vec2(output.logical_size.0 as f32, output.logical_size.1 as f32),
+ output.transform,
+ )),
+ });
+
+ let axis = Vec3::new(0., 0., 1.);
+
+ let angle = match output.transform {
+ Transform::_90 | Transform::Flipped90 => PI / 2.,
+ Transform::_180 | Transform::Flipped180 => PI,
+ Transform::_270 | Transform::Flipped270 => -PI / 2.,
+ _ => 0.,
+ };
+
+ Some(OverlayData {
+ state: OverlayState {
+ name: output.name.clone(),
+ size,
+ want_visible: idx == 0,
+ show_hide: true,
+ grabbable: true,
+ spawn_rotation: Quat::from_axis_angle(axis, angle),
+ ..Default::default()
+ },
+ backend,
+ ..Default::default()
+ })
+ } else {
+ warn!("{}: Will not be used", &output.name);
+ None
+ }
+}
+
+pub fn get_screens_wayland(session: &AppSession) -> Vec>
+where
+ O: Default,
+{
+ let mut overlays = vec![];
+ let wl = WlxClient::new().unwrap();
+
+ for idx in 0..wl.outputs.len() {
+ if let Some(overlay) = try_create_screen(&wl, idx, &session) {
+ overlays.push(overlay);
+ }
+ }
+ overlays
+}
+
+pub fn get_screens_x11() -> Vec>
+where
+ O: Default,
+{
+ todo!()
+}
diff --git a/src/overlays/watch.rs b/src/overlays/watch.rs
index 98922e02..7f66ac94 100644
--- a/src/overlays/watch.rs
+++ b/src/overlays/watch.rs
@@ -1,19 +1,20 @@
use std::{sync::Arc, time::Instant};
use chrono::Local;
-use glam::{Quat, Vec3};
use crate::{
+ backend::{
+ common::{OverlaySelector, TaskType},
+ overlay::{OverlayData, OverlayState, RelativeTo},
+ },
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 {
+pub fn create_watch(state: &AppState, screens: &[OverlayData]) -> OverlayData
+where
+ O: Default,
+{
let mut canvas = CanvasBuilder::new(400, 200, state.graphics.clone(), state.format, ());
let empty_str: Arc = Arc::from("");
@@ -93,22 +94,19 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc)>) -> Overla
.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;
- }
- }
- }));
+ app.tasks.enqueue(TaskType::Overlay(
+ OverlaySelector::Name("Kbd".into()),
+ Box::new(|_app, o| {
+ o.want_visible = !o.want_visible;
+ }),
+ ));
} else {
- app.tasks.push_back(Box::new(|app, o| {
- for overlay in o {
- if &*overlay.name == "Kbd" {
- overlay.reset(app);
- }
- }
- }));
+ app.tasks.enqueue(TaskType::Overlay(
+ OverlaySelector::Name("Kbd".into()),
+ Box::new(|app, o| {
+ o.reset(app);
+ }),
+ ));
}
}
});
@@ -116,11 +114,17 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc)>) -> Overla
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);
+ for screen in screens.into_iter() {
+ let button = canvas.button(
+ button_x + 2.,
+ 162.,
+ button_width - 4.,
+ 36.,
+ screen.state.name.clone(),
+ );
button.state = Some(WatchButtonState {
pressed_at: Instant::now(),
- scr_idx,
+ scr_idx: screen.state.id,
});
button.on_press = Some(|control, _data, _app| {
@@ -136,13 +140,19 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc)>) -> Overla
.as_millis()
< 2000
{
- app.tasks.push_back(Box::new(move |_app, o| {
- o[scr_idx].want_visible = !o[scr_idx].want_visible;
- }));
+ app.tasks.enqueue(TaskType::Overlay(
+ OverlaySelector::Id(scr_idx),
+ Box::new(|_app, o| {
+ o.want_visible = !o.want_visible;
+ }),
+ ));
} else {
- app.tasks.push_back(Box::new(move |app, o| {
- o[scr_idx].reset(app);
- }));
+ app.tasks.enqueue(TaskType::Overlay(
+ OverlaySelector::Id(scr_idx),
+ Box::new(|app, o| {
+ o.reset(app);
+ }),
+ ));
}
}
});
@@ -152,14 +162,17 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc)>) -> Overla
let relative_to = RelativeTo::Hand(state.session.watch_hand);
OverlayData {
- name: "Watch".into(),
- size: (400, 200),
- width: 0.065,
+ state: OverlayState {
+ name: "Watch".into(),
+ size: (400, 200),
+ width: 0.065,
+ want_visible: true,
+ spawn_point: state.session.watch_pos.into(),
+ spawn_rotation: state.session.watch_rot,
+ relative_to,
+ ..Default::default()
+ },
backend: Box::new(canvas.build()),
- want_visible: true,
- relative_to,
- spawn_point: state.session.watch_pos,
- spawn_rotation: state.session.watch_rot,
..Default::default()
}
}
diff --git a/src/ovr.rs b/src/ovr.rs
deleted file mode 100644
index 9831f5d5..00000000
--- a/src/ovr.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-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/actions_binding_knuckles.json b/src/res/actions_binding_knuckles.json
index 1f19b086..23d41c89 100644
--- a/src/res/actions_binding_knuckles.json
+++ b/src/res/actions_binding_knuckles.json
@@ -1,6 +1,6 @@
{
"action_manifest_version" : 0,
- "app_key" : "galister.wlxoverlay",
+ "app_key" : "galister.wlxoverlay-s",
"bindings" : {
"/actions/default" : {
"haptics" : [
diff --git a/src/res/actions_binding_oculus.json b/src/res/actions_binding_oculus.json
index 37c82684..5f252022 100644
--- a/src/res/actions_binding_oculus.json
+++ b/src/res/actions_binding_oculus.json
@@ -1,6 +1,6 @@
{
"action_manifest_version" : 0,
- "app_key" : "galister.wlxoverlay",
+ "app_key" : "galister.wlxoverlay-s",
"bindings" : {
"/actions/default" : {
"haptics" : [
diff --git a/src/res/actions_binding_vive.json b/src/res/actions_binding_vive.json
index a3db7d80..b1013c14 100644
--- a/src/res/actions_binding_vive.json
+++ b/src/res/actions_binding_vive.json
@@ -1,6 +1,6 @@
{
"action_manifest_version" : 0,
- "app_key" : "galister.wlxoverlay",
+ "app_key" : "galister.wlxoverlay-s",
"bindings" : {
"/actions/default": {
"haptics" : [
diff --git a/src/state.rs b/src/state.rs
index 6eeb1b73..c2df4c6b 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,27 +1,50 @@
-use std::{collections::VecDeque, env::VarError, path::Path, sync::Arc};
+use std::{env::VarError, path::Path, sync::Arc};
use glam::{Quat, Vec3};
use log::warn;
+use vulkano::{
+ device::{physical::PhysicalDevice, DeviceExtensions},
+ format::Format,
+ instance::InstanceExtensions,
+};
use crate::{
- graphics::WlxGraphics, gui::font::FontCache, input::InputProvider, overlays::OverlayData,
+ backend::common::TaskContainer, graphics::WlxGraphics, gui::font::FontCache,
+ input::InputProvider,
};
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 tasks: TaskContainer,
pub graphics: Arc,
pub format: vulkano::format::Format,
pub input: Box,
}
+impl AppState {
+ pub fn new(
+ vk_instance_extensions: InstanceExtensions,
+ vk_device_extensions_fn: impl FnMut(&PhysicalDevice) -> DeviceExtensions,
+ ) -> Self {
+ let (graphics, _event_loop) =
+ WlxGraphics::new(vk_instance_extensions, vk_device_extensions_fn);
+
+ AppState {
+ fc: FontCache::new(),
+ session: AppSession::load(),
+ tasks: TaskContainer::new(),
+ graphics: graphics.clone(),
+ format: Format::R8G8B8A8_UNORM,
+ input: crate::input::initialize_input(),
+ }
+ }
+}
+
pub struct AppSession {
pub config_path: String,
@@ -50,7 +73,7 @@ pub struct AppSession {
}
impl AppSession {
- pub fn load() -> AppSession {
+ pub fn load() -> Self {
let config_path = std::env::var("XDG_CONFIG_HOME")
.or_else(|_| std::env::var("HOME").map(|home| format!("{}/.config", home)))
.or_else(|_| {