Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd1c6ed0cf | ||
|
|
13c6dbfd4d | ||
|
|
4fb7c3df84 | ||
|
|
9d0e1ce021 | ||
|
|
dd38b590c1 | ||
|
|
24b7195297 | ||
|
|
7d8993b640 | ||
|
|
4c70ded2b0 | ||
|
|
7f7a8b5264 | ||
|
|
43246900db | ||
|
|
b7a123f9c9 | ||
|
|
900316968a | ||
|
|
db30f8e61b | ||
|
|
0a005b9864 | ||
|
|
f4ed8bc37d | ||
|
|
49ee4d3b67 | ||
|
|
c2f1f737a0 | ||
|
|
c9a57773d1 | ||
|
|
68a7c06b9e | ||
|
|
b196cbfa3a | ||
|
|
7067d048d6 | ||
|
|
ef09b69378 | ||
|
|
c5dea3b7c9 | ||
|
|
5ea147f9fe | ||
|
|
3d6fceb0dd | ||
|
|
b1900de652 | ||
|
|
76ff476112 | ||
|
|
57f9516a81 | ||
|
|
fe6ed81255 | ||
|
|
173b033963 | ||
|
|
fe9ae8225c | ||
|
|
a149098044 | ||
|
|
2a5bddbb5a | ||
|
|
a7d5992b6b | ||
|
|
94b9b9ddcf | ||
|
|
cfb193251f | ||
|
|
14e899db0e | ||
|
|
42fc3c3f44 | ||
|
|
9bfbade9a2 | ||
|
|
3f4002881c | ||
|
|
8a8121f1a8 | ||
|
|
8fc017a6fc | ||
|
|
7016904adb | ||
|
|
93692f365e | ||
|
|
b765b68d41 | ||
|
|
5d82e42820 | ||
|
|
15fe997237 | ||
|
|
44a3480022 | ||
|
|
f0c50ba237 | ||
|
|
30a05a3218 | ||
|
|
779706d792 | ||
|
|
d65163553e | ||
|
|
33ccc66411 | ||
|
|
fb1627dccc | ||
|
|
9f49ba729d | ||
|
|
a44f36641e | ||
|
|
34fd7e6e49 | ||
|
|
a5f087d29f | ||
|
|
3b996c46e2 | ||
|
|
2e50491144 | ||
|
|
c3e4b2ed2a | ||
|
|
c0141da88b | ||
|
|
8f18d83694 | ||
|
|
6822e4bdb7 | ||
|
|
3c66109c45 | ||
|
|
d27ec84496 | ||
|
|
239e0c0318 | ||
|
|
ab913a8d84 | ||
|
|
a5653853f8 | ||
|
|
58a17fedba | ||
|
|
be709efbdd | ||
|
|
4f01bd5eec | ||
|
|
242eed37fe | ||
|
|
fe22d3954a | ||
|
|
96b4e22e10 | ||
|
|
a51db703fd | ||
|
|
7e755a44b8 | ||
|
|
80c9386f79 | ||
|
|
4730f0732b | ||
|
|
79935befb7 | ||
|
|
b2e452326b |
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@@ -3,7 +3,7 @@ name: Build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- "*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_package:
|
build_and_package:
|
||||||
@@ -11,13 +11,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install runtime dependencies
|
- name: Install runtime dependencies
|
||||||
run: sudo apt install -y --no-install-recommends libxkbcommon-dev libstdc++6 libopenxr-dev libx11-dev libxfixes-dev libgl1-mesa-dev libegl1-mesa-dev libgbm-dev libfontconfig-dev libjsoncpp-dev libxcb1-dev libglx-dev libxcb-glx0-dev libdrm-dev libwayland-dev libfreetype-dev libpng-dev
|
run: sudo apt install -y --no-install-recommends libxkbcommon-dev libstdc++6 libopenxr-dev libx11-dev libxfixes-dev libgl1-mesa-dev libegl1-mesa-dev libgbm-dev libfontconfig-dev libjsoncpp-dev libxcb1-dev libglx-dev libxcb-glx0-dev libdrm-dev libwayland-dev libfreetype-dev libpng-dev
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: sudo apt install -y --no-install-recommends cmake ninja-build
|
run: sudo apt install -y --no-install-recommends cmake ninja-build libfuse2
|
||||||
|
|
||||||
- name: Set up Rust
|
- name: Set up Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@@ -26,21 +26,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Build server
|
- name: Build server
|
||||||
run: cargo build --release
|
run: cargo build --release
|
||||||
|
|
||||||
|
|
||||||
- name: Install appimagetool
|
|
||||||
run: |
|
|
||||||
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$(uname -m).AppImage -O /usr/local/bin/appimagetool; \
|
|
||||||
chmod +x /usr/local/bin/appimagetool; \
|
|
||||||
sed -i 's|AI\x02|\x00\x00\x00|' /usr/local/bin/appimagetool
|
|
||||||
- name: Install cargo-appimage
|
|
||||||
run: cargo install cargo-appimage
|
|
||||||
|
|
||||||
- name: Generate AppImage
|
|
||||||
run: cargo appimage
|
|
||||||
|
|
||||||
- name: Upload AppImage
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: appimage
|
|
||||||
path: '*.AppImage'
|
|
||||||
367
Cargo.lock
generated
367
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
@@ -50,7 +50,7 @@ dependencies = [
|
|||||||
"ndk-context",
|
"ndk-context",
|
||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
"num_enum 0.7.3",
|
"num_enum 0.7.3",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -87,16 +87,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "angular-units"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4f1c36cde4b75aa8518ad38880fdc7b649d7bf22b359a296964756e2319d598d"
|
|
||||||
dependencies = [
|
|
||||||
"approx 0.3.2",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.15"
|
||||||
@@ -158,15 +148,6 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e149dc73cd30538307e7ffa2acd3d2221148eaeed4871f246657b1c3eaa1cbd2"
|
checksum = "e149dc73cd30538307e7ffa2acd3d2221148eaeed4871f246657b1c3eaa1cbd2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "approx"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "approx"
|
name = "approx"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -217,7 +198,7 @@ checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-task",
|
"async-task",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"fastrand 2.1.1",
|
"fastrand",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
@@ -291,7 +272,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -331,7 +312,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -348,7 +329,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -470,15 +451,6 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blocking"
|
name = "blocking"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@@ -509,7 +481,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -578,7 +550,7 @@ version = "0.18.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"approx 0.4.0",
|
"approx",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -613,7 +585,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -648,10 +620,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
|
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
|
"color-spantrace",
|
||||||
"eyre",
|
"eyre",
|
||||||
"indenter",
|
"indenter",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
|
"tracing-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color-spantrace"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"owo-colors",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-error",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -727,15 +713,6 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.2.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@@ -766,16 +743,6 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-common"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cty"
|
name = "cty"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -789,13 +756,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "dashmap"
|
||||||
version = "0.10.7"
|
version = "6.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"cfg-if",
|
||||||
"crypto-common",
|
"crossbeam-utils",
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"lock_api",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot_core 0.9.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -866,22 +837,23 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drm"
|
name = "drm"
|
||||||
version = "0.12.0"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1"
|
checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"drm-ffi",
|
"drm-ffi",
|
||||||
"drm-fourcc",
|
"drm-fourcc",
|
||||||
|
"libc",
|
||||||
"rustix",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drm-ffi"
|
name = "drm-ffi"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53"
|
checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"drm-sys",
|
"drm-sys",
|
||||||
"rustix",
|
"rustix",
|
||||||
@@ -895,9 +867,9 @@ checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drm-sys"
|
name = "drm-sys"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986"
|
checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.6.5",
|
"linux-raw-sys 0.6.5",
|
||||||
@@ -942,7 +914,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1002,15 +974,6 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fastrand"
|
|
||||||
version = "1.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
|
||||||
dependencies = [
|
|
||||||
"instant",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
@@ -1085,11 +1048,11 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-lite"
|
name = "futures-lite"
|
||||||
version = "2.3.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
|
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand 2.1.1",
|
"fastrand",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"parking",
|
"parking",
|
||||||
@@ -1104,7 +1067,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1126,11 +1089,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
@@ -1149,16 +1109,6 @@ dependencies = [
|
|||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-array"
|
|
||||||
version = "0.14.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@@ -1475,7 +1425,7 @@ dependencies = [
|
|||||||
"combine",
|
"combine",
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
@@ -1503,7 +1453,7 @@ checksum = "062c875482ccb676fd40c804a40e3824d4464c18c364547456d1c8e8e951ae47"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"miette",
|
"miette",
|
||||||
"nom",
|
"nom",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1624,7 +1574,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex-syntax 0.8.4",
|
"regex-syntax 0.8.4",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1658,7 +1608,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1708,7 +1658,7 @@ checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"miette-derive",
|
"miette-derive",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1720,7 +1670,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1797,7 +1747,7 @@ dependencies = [
|
|||||||
"raw-window-handle 0.4.3",
|
"raw-window-handle 0.4.3",
|
||||||
"raw-window-handle 0.5.2",
|
"raw-window-handle 0.5.2",
|
||||||
"raw-window-handle 0.6.2",
|
"raw-window-handle 0.6.2",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1926,7 +1876,7 @@ dependencies = [
|
|||||||
"proc-macro-crate 3.2.0",
|
"proc-macro-crate 3.2.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2091,7 +2041,7 @@ dependencies = [
|
|||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2137,7 +2087,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2159,7 +2109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
|
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"fastrand 2.1.1",
|
"fastrand",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2184,12 +2134,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "portable-atomic"
|
|
||||||
version = "1.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
@@ -2205,17 +2149,6 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prisma"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f5640533116b656900156ef15e22d3789edb9a71f36ec04a2678a307be243495"
|
|
||||||
dependencies = [
|
|
||||||
"angular-units",
|
|
||||||
"approx 0.3.2",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@@ -2260,7 +2193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2283,7 +2216,7 @@ dependencies = [
|
|||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2343,15 +2276,6 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "random-string"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f70fd13c3024ae3f17381bb5c4d409c6dc9ea6895c08fa2147aba305bea3c4af"
|
|
||||||
dependencies = [
|
|
||||||
"fastrand 1.9.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -2399,7 +2323,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2501,12 +2425,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scan_fmt"
|
|
||||||
version = "0.2.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -2548,7 +2466,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2571,7 +2489,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2583,17 +2501,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha1"
|
|
||||||
version = "0.10.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -2663,8 +2570,8 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/smithay/smithay.git#656178be0a19ae4c577c9c93a3d4ebfdb80e649c"
|
source = "git+https://github.com/smithay/smithay.git#0c2230f858580b52d628087d6dae1795278b8756"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
@@ -2678,17 +2585,14 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"gl_generator",
|
"gl_generator",
|
||||||
"indexmap 2.5.0",
|
"indexmap 2.5.0",
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
"libc",
|
||||||
"libloading",
|
"libloading",
|
||||||
"once_cell",
|
|
||||||
"profiling",
|
"profiling",
|
||||||
"rand",
|
"rand",
|
||||||
"rustix",
|
"rustix",
|
||||||
"scan_fmt",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"wayland-protocols-misc",
|
"wayland-protocols-misc",
|
||||||
@@ -2716,9 +2620,10 @@ checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr"
|
name = "stardust-xr"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
source = "git+https://github.com/StardustXR/core.git#1bc94e67cad6b69fa3c509598b07bc5085cc65a3"
|
source = "git+https://github.com/StardustXR/core.git?branch=dev#bce6ec660f026c577156b3cff41f9312c1caa1d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cluFlock",
|
"cluFlock",
|
||||||
|
"color-eyre",
|
||||||
"dirs",
|
"dirs",
|
||||||
"global_counter",
|
"global_counter",
|
||||||
"mint",
|
"mint",
|
||||||
@@ -2728,7 +2633,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"shiva-color-rs",
|
"shiva-color-rs",
|
||||||
"stardust-xr-schemas",
|
"stardust-xr-schemas",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@@ -2736,7 +2641,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr-schemas"
|
name = "stardust-xr-schemas"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
source = "git+https://github.com/StardustXR/core.git#1bc94e67cad6b69fa3c509598b07bc5085cc65a3"
|
source = "git+https://github.com/StardustXR/core.git?branch=dev#bce6ec660f026c577156b3cff41f9312c1caa1d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flatbuffers",
|
"flatbuffers",
|
||||||
"flexbuffers",
|
"flexbuffers",
|
||||||
@@ -2744,10 +2649,10 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"kdl",
|
"kdl",
|
||||||
"manifest-dir-macros",
|
"manifest-dir-macros",
|
||||||
"random-string",
|
"nanoid",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
"tokio",
|
"tokio",
|
||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
@@ -2759,19 +2664,15 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
|
"dashmap",
|
||||||
"directories",
|
"directories",
|
||||||
"glam",
|
"glam",
|
||||||
"global_counter",
|
"global_counter",
|
||||||
"input-event-codes",
|
"input-event-codes",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
|
||||||
"mint",
|
"mint",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
"nix 0.29.0",
|
|
||||||
"once_cell",
|
|
||||||
"parking_lot 0.12.3",
|
"parking_lot 0.12.3",
|
||||||
"portable-atomic",
|
|
||||||
"prisma",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"send_wrapper",
|
"send_wrapper",
|
||||||
@@ -2782,6 +2683,7 @@ dependencies = [
|
|||||||
"stardust-xr",
|
"stardust-xr",
|
||||||
"stardust-xr-server-codegen",
|
"stardust-xr-server-codegen",
|
||||||
"stereokit-rust",
|
"stereokit-rust",
|
||||||
|
"thiserror 2.0.9",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -2814,12 +2716,12 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stereokit-macros"
|
name = "stereokit-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/mvvvv/StereoKit-rust.git#1a0ba771a948e31044f0dd264efcc9f5be4682d5"
|
source = "git+https://github.com/mvvvv/StereoKit-rust.git?rev=73ffaae6f42aa369e599a6ea0391f77840d682d8#73ffaae6f42aa369e599a6ea0391f77840d682d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stereokit-rust"
|
name = "stereokit-rust"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/mvvvv/StereoKit-rust.git#1a0ba771a948e31044f0dd264efcc9f5be4682d5"
|
source = "git+https://github.com/mvvvv/StereoKit-rust.git?rev=73ffaae6f42aa369e599a6ea0391f77840d682d8#73ffaae6f42aa369e599a6ea0391f77840d682d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-activity",
|
"android-activity",
|
||||||
"android_logger",
|
"android_logger",
|
||||||
@@ -2833,7 +2735,7 @@ dependencies = [
|
|||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
"openxr-sys",
|
"openxr-sys",
|
||||||
"stereokit-macros",
|
"stereokit-macros",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2871,7 +2773,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2887,9 +2789,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.77"
|
version = "2.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2915,7 +2817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand 2.1.1",
|
"fastrand",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@@ -2938,7 +2840,16 @@ version = "1.0.63"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.63",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2949,7 +2860,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2998,7 +2920,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3151,7 +3073,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3164,6 +3086,16 @@ dependencies = [
|
|||||||
"valuable",
|
"valuable",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-error"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-log"
|
name = "tracing-log"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -3230,12 +3162,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uds_windows"
|
name = "uds_windows"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -3339,9 +3265,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-protocols"
|
name = "wayland-protocols"
|
||||||
version = "0.32.4"
|
version = "0.32.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0"
|
checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
@@ -3388,9 +3314,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-server"
|
name = "wayland-server"
|
||||||
version = "0.31.5"
|
version = "0.31.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f18d47038c0b10479e695d99ed073e400ccd9bdbb60e6e503c96f62adcb12b6"
|
checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
@@ -3473,7 +3399,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3484,7 +3410,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3738,6 +3664,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xdg-home"
|
name = "xdg-home"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -3776,7 +3711,7 @@ dependencies = [
|
|||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
"unicase",
|
"unicase",
|
||||||
"xkbcommon-rs-codegen",
|
"xkbcommon-rs-codegen",
|
||||||
"xkeysym",
|
"xkeysym",
|
||||||
@@ -3809,9 +3744,9 @@ checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "4.4.0"
|
version = "5.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
|
checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"async-executor",
|
"async-executor",
|
||||||
@@ -3826,20 +3761,18 @@ dependencies = [
|
|||||||
"enumflags2",
|
"enumflags2",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-lite",
|
||||||
"futures-util",
|
|
||||||
"hex",
|
"hex",
|
||||||
"nix 0.29.0",
|
"nix 0.29.0",
|
||||||
"ordered-stream",
|
"ordered-stream",
|
||||||
"rand",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"sha1",
|
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uds_windows",
|
"uds_windows",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
|
"winnow 0.7.4",
|
||||||
"xdg-home",
|
"xdg-home",
|
||||||
"zbus_macros",
|
"zbus_macros",
|
||||||
"zbus_names",
|
"zbus_names",
|
||||||
@@ -3848,22 +3781,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus_macros"
|
name = "zbus_macros"
|
||||||
version = "4.4.0"
|
version = "5.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
|
checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate 3.2.0",
|
"proc-macro-crate 3.2.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
|
"zbus_names",
|
||||||
|
"zvariant",
|
||||||
"zvariant_utils",
|
"zvariant_utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus_names"
|
name = "zbus_names"
|
||||||
version = "3.0.0"
|
version = "4.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
|
checksum = "cdc27fbd3593ff015cef906527a2ec4115e2e3dbf6204a24d952ac4975c80614"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
@@ -3888,42 +3823,46 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant"
|
name = "zvariant"
|
||||||
version = "4.2.0"
|
version = "5.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
|
checksum = "c690a1da8858fd4377b8cc3134a753b0bea1d8ebd78ad6e5897fab821c5e184e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"endi",
|
"endi",
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
"serde",
|
"serde",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"zvariant_derive",
|
"zvariant_derive",
|
||||||
|
"zvariant_utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant_derive"
|
name = "zvariant_derive"
|
||||||
version = "4.2.0"
|
version = "5.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
|
checksum = "83b6ddc1fed08493e4f2bd9350e7d00a3383467228735f3f169a9f8820fde755"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate 3.2.0",
|
"proc-macro-crate 3.2.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.87",
|
||||||
"zvariant_utils",
|
"zvariant_utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant_utils"
|
name = "zvariant_utils"
|
||||||
version = "2.1.0"
|
version = "3.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
|
checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"serde",
|
||||||
|
"static_assertions",
|
||||||
|
"syn 2.0.87",
|
||||||
|
"winnow 0.7.4",
|
||||||
]
|
]
|
||||||
|
|||||||
25
Cargo.toml
25
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
rust-version = "1.75"
|
rust-version = "1.85"
|
||||||
name = "stardust-xr-server"
|
name = "stardust-xr-server"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
authors = ["Nova King <technobaboo@proton.me>"]
|
authors = ["Nova King <technobaboo@proton.me>"]
|
||||||
@@ -14,6 +14,7 @@ members = ["codegen"]
|
|||||||
|
|
||||||
[workspace.dependencies.stardust-xr]
|
[workspace.dependencies.stardust-xr]
|
||||||
git = "https://github.com/StardustXR/core.git"
|
git = "https://github.com/StardustXR/core.git"
|
||||||
|
branch = "dev"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "stardust-xr-server"
|
name = "stardust-xr-server"
|
||||||
@@ -40,25 +41,24 @@ auto_link_exclude_list = [
|
|||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = true
|
debug = true
|
||||||
strip = "none"
|
strip = false
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = "line-tables-only"
|
debug = "line-tables-only"
|
||||||
strip = "none"
|
strip = true
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
|
lto = "thin"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# small utility thingys
|
# small utility thingys
|
||||||
once_cell = "1.19.0"
|
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rustc-hash = "2.0.0"
|
rustc-hash = "2.0.0"
|
||||||
portable-atomic = { version = "1.7.0", features = ["float", "std"] }
|
|
||||||
send_wrapper = "0.6.0"
|
send_wrapper = "0.6.0"
|
||||||
slotmap = "1.0.7"
|
slotmap = "1.0.7"
|
||||||
global_counter = "=0.2.2"
|
global_counter = "=0.2.2"
|
||||||
@@ -68,6 +68,7 @@ parking_lot = "0.12.3"
|
|||||||
color-eyre = { version = "0.6.3", default-features = false }
|
color-eyre = { version = "0.6.3", default-features = false }
|
||||||
clap = { version = "4.5.13", features = ["derive"] }
|
clap = { version = "4.5.13", features = ["derive"] }
|
||||||
console-subscriber = { version = "0.4.0", optional = true }
|
console-subscriber = { version = "0.4.0", optional = true }
|
||||||
|
thiserror = "2.0.9"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
tracing-tracy = { version = "0.11.1", optional = true }
|
tracing-tracy = { version = "0.11.1", optional = true }
|
||||||
@@ -81,34 +82,28 @@ toml = "0.8.19"
|
|||||||
glam = { version = "0.29.0", features = ["mint", "serde"] }
|
glam = { version = "0.29.0", features = ["mint", "serde"] }
|
||||||
mint = "0.5.9"
|
mint = "0.5.9"
|
||||||
tokio = { version = "1.39.2", features = ["rt-multi-thread", "signal", "time"] }
|
tokio = { version = "1.39.2", features = ["rt-multi-thread", "signal", "time"] }
|
||||||
prisma = "0.1.1"
|
|
||||||
|
|
||||||
# linux stuffs
|
# linux stuffs
|
||||||
libc = "0.2.155"
|
|
||||||
nix = "0.29.0"
|
|
||||||
input-event-codes = "6.2.0"
|
input-event-codes = "6.2.0"
|
||||||
zbus = { version = "4.4.0", default-features = false, features = ["tokio"] }
|
zbus = { version = "5.0.0", default-features = false, features = ["tokio"] }
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
xkbcommon-rs = "0.1.0"
|
xkbcommon-rs = "0.1.0"
|
||||||
|
|
||||||
# wayland
|
# wayland
|
||||||
wayland-backend = { version = "0.3.7", optional = true, default-features = false }
|
wayland-backend = { version = "0.3.7", optional = true, default-features = false }
|
||||||
wayland-scanner = { version = "0.31.4", optional = true }
|
wayland-scanner = { version = "0.31.4", optional = true }
|
||||||
|
dashmap = "6.1.0"
|
||||||
|
|
||||||
[dependencies.smithay]
|
[dependencies.smithay]
|
||||||
# git = "https://github.com/technobaboo/smithay.git"
|
|
||||||
# git = "https://github.com/colinmarc/smithay.git"
|
|
||||||
git = "https://github.com/smithay/smithay.git"
|
git = "https://github.com/smithay/smithay.git"
|
||||||
# path = "../smithay"
|
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"]
|
features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
|
||||||
[dependencies.stereokit-rust]
|
[dependencies.stereokit-rust]
|
||||||
# path = "../StereoKit-rust"
|
|
||||||
git = "https://github.com/mvvvv/StereoKit-rust.git"
|
git = "https://github.com/mvvvv/StereoKit-rust.git"
|
||||||
# git = "https://github.com/technobaboo/StereoKit-rust.git"
|
rev = "73ffaae6f42aa369e599a6ea0391f77840d682d8"
|
||||||
features = ["no-event-loop"]
|
features = ["no-event-loop"]
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
|||||||
119
README.md
119
README.md
@@ -1,99 +1,48 @@
|
|||||||
# Stardust XR Server
|
# Stardust XR Server
|
||||||
|
|
||||||
Stardust XR is a display server for VR and AR headsets on Linux-based systems. [Stardust provides a 3D environment](https://www.youtube.com/watch?v=v2WblwbaLaA), where anything from 2D windows (including your existing apps!), to 3D apps built from objects, can exist together in physical space.
|
Stardust XR is a display server for VR and AR headsets on Linux-based systems. [Stardust provides a 3D environment](https://www.youtube.com/watch?v=v2WblwbaLaA), where anything from 2D windows (including your existing apps!), to 3D apps built from objects, can exist together in physical space.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Core Dependencies
|
|
||||||
| **Dependency** | **Ubuntu/Debian** | **Arch Linux** | **Fedora** |
|
|
||||||
|-----------------------------|-------------------------------------------------------------------------------------------------|---------------------------------------------------|-------------------------------------------------------------|
|
|
||||||
| **Cargo** | `cargo` | `cargo` | `cargo` |
|
|
||||||
| **CMake** | `cmake` | `cmake` | `cmake` |
|
|
||||||
| **EGL+GLES 3.2** | `libegl1-mesa-dev`, `libgles2-mesa-dev` | `mesa` *(provides EGL/GLES libraries and headers)* | `mesa-libEGL-devel`, `mesa-libGLES-devel` |
|
|
||||||
| **GLX+Xlib** | `libx11-dev`, `libxfixes-dev`, `libxcb1-dev`, `libgl1-mesa-dev`, `libxkbcommon-dev` | `libx11`, `libxfixes`, `libxcb` *(and GLX via mesa)*| `libX11-devel`, `libXfixes-devel`, `libxcb-devel`, `mesa-libGL-devel` *(or equivalent)* |
|
|
||||||
| **fontconfig** | `libfontconfig1-dev` | `fontconfig` | `fontconfig-devel` |
|
|
||||||
| **dlopen** (glibc function) | Provided by `libc6-dev` (part of the core C library) | Provided by `glibc` *(included in base-devel)* | Provided by `glibc-devel` |
|
|
||||||
| **OpenXR Loader** | `libopenxr-loader1`, `libopenxr-dev`, `libopenxr1-monado` | `openxr` | `openxr-devel` |
|
|
||||||
|
|
||||||
Command line installation of core & dynamic dependencies are provided below:
|
Command line installation of core & dynamic dependencies are provided below:
|
||||||
<details>
|
<details>
|
||||||
<summary>Ubuntu/Debian</summary>
|
<summary>Ubuntu/Debian</summary>
|
||||||
<pre><code class="language-bash">
|
<pre><code class="language-bash">
|
||||||
sudo apt-get update && sudo apt-get install -y \
|
sudo apt update && sudo apt install \
|
||||||
build-essential \
|
build-essential \
|
||||||
cargo \
|
cargo \
|
||||||
cmake \
|
cmake \
|
||||||
libegl1-mesa-dev libgles2-mesa-dev \
|
libxkbcommon-dev libudev1 libinput10 libcap2 libmtdev1 libevdev2 libwacom9 libgudev-1.0-0 \
|
||||||
libx11-dev libxfixes-dev libxcb1-dev libxau-dev libgl1-mesa-dev libxkbcommon-dev \
|
libglib2.0-dev libffi8 libpcre2-dev libxkbcommon-x11-dev libxcb-dev libxcb-xkb-dev libxau-dev \
|
||||||
libfontconfig1-dev libfreetype6-dev libharfbuzz-dev libgraphite2-dev \
|
libstdc++-dev libx11-dev libxfixes-dev libegl-dev libgbm-dev libfontconfig1-dev libgl-dev \
|
||||||
libc6-dev \
|
libdrm-dev libexpat1-dev libfreetype6-dev libxml2-dev zlib1g-dev libbz2-dev libpng-dev \
|
||||||
libopenxr-loader1 libopenxr-dev libopenxr1-monado libwayland-dev \
|
libharfbuzz-dev libbrotli-dev liblzma-dev libraphite2-dev
|
||||||
libjsoncpp-dev libdrm-dev libexpat1-dev libxcb-randr0-dev \
|
|
||||||
libxml2-dev libffi-dev libbz2-dev libpng-dev libbrotli-dev liblzma-dev libglib2.0-dev libpcre2-dev
|
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Arch Linux</summary>
|
<summary>Fedora</summary>
|
||||||
<pre><code class="language-bash">
|
<pre><code class="language-bash">
|
||||||
sudo pacman -Syu --needed \
|
sudo apt update && sudo apt install \
|
||||||
base-devel \
|
|
||||||
rust \
|
|
||||||
cmake \
|
|
||||||
mesa \
|
|
||||||
libx11 \
|
|
||||||
libxfixes \
|
|
||||||
libxcb \
|
|
||||||
libxkbcommon \
|
|
||||||
fontconfig \
|
|
||||||
freetype2 \
|
|
||||||
openxr \
|
|
||||||
jsoncpp \
|
|
||||||
libffi \
|
|
||||||
wayland \
|
|
||||||
expat \
|
|
||||||
libxml2 \
|
|
||||||
libxau \
|
|
||||||
bzip2 \
|
|
||||||
xz \
|
|
||||||
libpng \
|
|
||||||
brotli \
|
|
||||||
pcre2 \
|
|
||||||
glib2 \
|
|
||||||
libdrm
|
|
||||||
</code></pre>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>Fedora</summary>
|
|
||||||
<pre><code class="language-bash">
|
|
||||||
sudo dnf group install development-tools && \
|
|
||||||
sudo dnf install -y \
|
|
||||||
cargo \
|
cargo \
|
||||||
cmake \
|
cmake \
|
||||||
mesa-libEGL-devel \
|
libxkbcommon-devel systemd-devel libinput-devel libcap-devel mtdev-devel libevdev-devel glib2-devel \
|
||||||
mesa-libGLES-devel \
|
libffi-devel pcre2-devel libxkbcommon-x11-devel libxcb-devel libXau-devel libstdc++-devel libx11-devel libxfixes-devel \
|
||||||
libX11-devel \
|
mesa-libEGL-devel mesa-libgbm-devel fontconfig-devel libdrm-devel expat-devel freetype-devel libxml2-devel zlib-devel \
|
||||||
libXfixes-devel \
|
bzip2-devel libpng-devel harfbuzz-devel brotli-devel xz-devel graphite2-devel
|
||||||
libxcb-devel \
|
</code></pre>
|
||||||
libxkbcommon-devel \
|
</details>
|
||||||
fontconfig-devel \
|
|
||||||
freetype-devel \
|
|
||||||
harfbuzz-devel \
|
<details>
|
||||||
graphite2-devel \
|
<summary>Arch Linux</summary>
|
||||||
openxr-devel \
|
<pre><code class="language-bash">
|
||||||
wayland-devel \
|
sudo pacman -Syu --needed \
|
||||||
jsoncpp-devel \
|
cargo \
|
||||||
libdrm-devel \
|
cmake \
|
||||||
expat-devel \
|
libxkbcommon systemd libinput libcap mtdev libevdev libwacom glib2 libffi pcre2 libxkbcommon-x11 \
|
||||||
xcb-util-devel \
|
libxcb libxau libx11 libxfixes mesa fontconfig libdrm expat freetype2 libxml2 zlib bzip2 \
|
||||||
libxml2-devel \
|
libpng harfbuzz brotli xz graphite
|
||||||
libXau-devel \
|
|
||||||
bzip2-devel \
|
|
||||||
xz-devel \
|
|
||||||
libpng-devel \
|
|
||||||
brotli-devel \
|
|
||||||
pcre2-devel \
|
|
||||||
glib2-devel
|
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@@ -118,7 +67,7 @@ We've provided a manual installation script [here](https://github.com/cybernetic
|
|||||||
|
|
||||||
After cloning the repository
|
After cloning the repository
|
||||||
```bash
|
```bash
|
||||||
cargo build
|
cargo build --release # this is needed to skip validation layers
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -133,32 +82,32 @@ First, try running `cargo run -- -f` in a terminal window to check out flatscree
|
|||||||
If there aren't already any clients running, you'll need to manually launch them by either navigating to their repositories and running `cargo run`, or running them via their names if you installed via dnf or the manual installation script, such as `flatland`, `hexagon_launcher`, etc.
|
If there aren't already any clients running, you'll need to manually launch them by either navigating to their repositories and running `cargo run`, or running them via their names if you installed via dnf or the manual installation script, such as `flatland`, `hexagon_launcher`, etc.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> [Flatland](https://github.com/StardustXR/flatland) must be running for 2D apps to launch.
|
> [Flatland](https://github.com/StardustXR/flatland) must be running for 2D apps to launch.
|
||||||
|
|
||||||
### Startup Script
|
### Startup Script
|
||||||
A startup script can be created at `~/.config/stardust/startup` that will launch specified settings and clients/applications, an example of which is shown [here](https://github.com/cyberneticmelon/usefulscripts/blob/main/startup). If you used the [installation script](https://github.com/cyberneticmelon/usefulscripts/blob/main/stardustxr_setup.sh), one will have already been made for you. This allows wide flexibility of what clients to launch upon startup (and, for example, *where*, using the [Gravity](https://github.com/StardustXR/gravity) client to specify X Y and Z co-ordinates).
|
A startup script can be created at `~/.config/stardust/startup` that will launch specified settings and clients/applications, an example of which is shown [here](https://github.com/cyberneticmelon/usefulscripts/blob/main/startup). If you used the [installation script](https://github.com/cyberneticmelon/usefulscripts/blob/main/stardustxr_setup.sh), one will have already been made for you. This allows wide flexibility of what clients to launch upon startup (and, for example, *where*, using the [Gravity](https://github.com/StardustXR/gravity) client to specify X Y and Z co-ordinates).
|
||||||
|
|
||||||
### Flatscreen Navigation
|
### Flatscreen Navigation
|
||||||
A video guide showcasing flatscreen controls is available [here](https://www.youtube.com/watch?v=JCYecSlKlDI)
|
A video guide showcasing flatscreen controls is available [here](https://www.youtube.com/watch?v=JCYecSlKlDI)
|
||||||
|
|
||||||
To move around, hold down `Shift + W A S D`, with `Q` for moving down and `E` for moving up.
|
To move around, hold down `Shift + W A S D`, with `Q` for moving down and `E` for moving up.
|
||||||

|

|
||||||
|
|
||||||
To look around, hold down `Shift + Right` Click while moving the mouse.
|
To look around, hold down `Shift + Right` Click while moving the mouse.
|
||||||

|

|
||||||
|
|
||||||
To drag applications out of the app launcher, hold down `Shift + ~`
|
To drag applications out of the app launcher, hold down `Shift + ~`
|
||||||

|

|
||||||
|
|
||||||
### XR Navigation
|
### XR Navigation
|
||||||
A video guide showcasing XR controls is available [here](https://www.youtube.com/watch?v=RbxFq6JjliA)
|
A video guide showcasing XR controls is available [here](https://www.youtube.com/watch?v=RbxFq6JjliA)
|
||||||
|
|
||||||
**Quest 3 Hand tracking**:
|
**Quest 3 Hand tracking**:
|
||||||
Pinch to drag and drop, grasp with full hand for grabbing, point and click with pointer finger to click or pinch from a distance
|
Pinch to drag and drop, grasp with full hand for grabbing, point and click with pointer finger to click or pinch from a distance
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Quest 3 Controller**:
|
**Quest 3 Controller**:
|
||||||
Grab with the grip buttons, click by touching the tip of the cones or by using the trigger from a distance
|
Grab with the grip buttons, click by touching the tip of the cones or by using the trigger from a distance
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -25,10 +25,6 @@ pub fn codegen_field_protocol(_input: proc_macro::TokenStream) -> proc_macro::To
|
|||||||
codegen_protocol(FIELD_PROTOCOL)
|
codegen_protocol(FIELD_PROTOCOL)
|
||||||
}
|
}
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn codegen_data_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
codegen_protocol(DATA_PROTOCOL)
|
|
||||||
}
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn codegen_audio_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn codegen_audio_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
codegen_protocol(AUDIO_PROTOCOL)
|
codegen_protocol(AUDIO_PROTOCOL)
|
||||||
}
|
}
|
||||||
@@ -64,11 +60,28 @@ fn codegen_protocol(protocol: &'static str) -> proc_macro::TokenStream {
|
|||||||
};
|
};
|
||||||
let aspect = generate_aspect(&Aspect {
|
let aspect = generate_aspect(&Aspect {
|
||||||
name: "interface".to_string(),
|
name: "interface".to_string(),
|
||||||
|
id: 0,
|
||||||
description: protocol.description.clone(),
|
description: protocol.description.clone(),
|
||||||
inherits: vec![],
|
inherits: vec![],
|
||||||
members: p.members,
|
members: p.members,
|
||||||
});
|
});
|
||||||
quote!(#node_id #aspect)
|
quote! {
|
||||||
|
#node_id
|
||||||
|
#aspect
|
||||||
|
pub struct Interface;
|
||||||
|
impl crate::nodes::AspectIdentifier for Interface {
|
||||||
|
impl_aspect_for_interface_aspect_id!{}
|
||||||
|
}
|
||||||
|
impl crate::nodes::Aspect for Interface {
|
||||||
|
impl_aspect_for_interface_aspect!{}
|
||||||
|
}
|
||||||
|
pub fn create_interface(client: &std::sync::Arc<crate::core::client::Client>) -> crate::core::error::Result<()>{
|
||||||
|
let node = crate::nodes::Node::from_id(client,INTERFACE_NODE_ID,false);
|
||||||
|
node.add_aspect(Interface);
|
||||||
|
node.add_to_scenegraph()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let custom_enums = protocol
|
let custom_enums = protocol
|
||||||
@@ -130,7 +143,7 @@ fn generate_custom_union(custom_union: &CustomUnion) -> TokenStream {
|
|||||||
quote! {
|
quote! {
|
||||||
#[doc = #description]
|
#[doc = #description]
|
||||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(tag = "t", content = "c")]
|
||||||
pub enum #name {#option_decls}
|
pub enum #name {#option_decls}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,11 +190,12 @@ fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
|||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
);
|
);
|
||||||
let client_side_members = client_members
|
let client_side_members = client_members
|
||||||
.map(generate_member)
|
.map(|m| generate_member(aspect.id, &aspect.name.to_case(Case::Snake), m))
|
||||||
.reduce(fold_tokens)
|
.reduce(fold_tokens)
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
// TODO: properly import all dependencies
|
// TODO: properly import all dependencies
|
||||||
quote! {
|
quote! {
|
||||||
|
#[allow(clippy::all)]
|
||||||
pub mod #client_mod_name {
|
pub mod #client_mod_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
#t
|
#t
|
||||||
@@ -190,11 +204,6 @@ fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
|||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let aspect_trait_name = Ident::new(
|
|
||||||
&format!("{}Aspect", &aspect.name.to_case(Case::Pascal)),
|
|
||||||
Span::call_site(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let opcodes = aspect
|
let opcodes = aspect
|
||||||
.members
|
.members
|
||||||
.iter()
|
.iter()
|
||||||
@@ -219,31 +228,95 @@ fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
|||||||
let alias_info = generate_alias_info(aspect);
|
let alias_info = generate_alias_info(aspect);
|
||||||
|
|
||||||
let server_side_members = server_members
|
let server_side_members = server_members
|
||||||
.map(generate_member)
|
.map(|m| generate_member(aspect.id, &aspect.name.to_case(Case::Pascal), m))
|
||||||
.reduce(fold_tokens)
|
.reduce(fold_tokens)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let add_node_members = aspect
|
let aspect_trait_name = Ident::new(
|
||||||
|
&format!("{}Aspect", &aspect.name.to_case(Case::Pascal)),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let run_signals = aspect
|
||||||
.members
|
.members
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|m| m.side == Side::Server)
|
.filter(|m| m.side == Side::Server)
|
||||||
.map(generate_handler)
|
.filter(|m| m._type == MemberType::Signal)
|
||||||
|
.map(|m| generate_run_member(&aspect_trait_name, MemberType::Signal, m))
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let run_methods = aspect
|
||||||
|
.members
|
||||||
|
.iter()
|
||||||
|
.filter(|m| m.side == Side::Server)
|
||||||
|
.filter(|m| m._type == MemberType::Method)
|
||||||
|
.map(|m| generate_run_member(&aspect_trait_name, MemberType::Method, m))
|
||||||
.reduce(fold_tokens)
|
.reduce(fold_tokens)
|
||||||
.map(|members| {
|
|
||||||
quote! {
|
|
||||||
fn add_node_members(node: &crate::nodes::Node) {
|
|
||||||
#members
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let server_side_members = quote! {
|
let server_side_members = quote! {
|
||||||
|
#[allow(clippy::all)]
|
||||||
#[doc = #description]
|
#[doc = #description]
|
||||||
pub trait #aspect_trait_name {
|
pub trait #aspect_trait_name {
|
||||||
#add_node_members
|
|
||||||
#server_side_members
|
#server_side_members
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
quote!(#opcodes #alias_info #client_side_members #server_side_members)
|
let aspect_id_macro_name = Ident::new(
|
||||||
|
&format!(
|
||||||
|
"impl_aspect_for_{}_aspect_id",
|
||||||
|
aspect.name.to_case(Case::Snake)
|
||||||
|
),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let aspect_macro_name = Ident::new(
|
||||||
|
&format!(
|
||||||
|
"impl_aspect_for_{}_aspect",
|
||||||
|
aspect.name.to_case(Case::Snake)
|
||||||
|
),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let aspect_id = aspect.id;
|
||||||
|
let aspect_macro = quote! {
|
||||||
|
macro_rules! #aspect_id_macro_name {
|
||||||
|
() => {
|
||||||
|
const ID: u64 = #aspect_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! #aspect_macro_name {
|
||||||
|
() => {
|
||||||
|
fn as_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[allow(clippy::all)]
|
||||||
|
fn run_signal(
|
||||||
|
&self,
|
||||||
|
_calling_client: std::sync::Arc<crate::core::client::Client>,
|
||||||
|
_node: std::sync::Arc<crate::nodes::Node>,
|
||||||
|
_signal: u64,
|
||||||
|
_message: crate::nodes::Message
|
||||||
|
) -> Result<(), stardust_xr::scenegraph::ScenegraphError> {
|
||||||
|
match _signal {
|
||||||
|
#run_signals
|
||||||
|
_ => Err(stardust_xr::scenegraph::ScenegraphError::MemberNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(clippy::all)]
|
||||||
|
fn run_method(
|
||||||
|
&self,
|
||||||
|
_calling_client: std::sync::Arc<crate::core::client::Client>,
|
||||||
|
_node: std::sync::Arc<crate::nodes::Node>,
|
||||||
|
_method: u64,
|
||||||
|
_message: crate::nodes::Message,
|
||||||
|
_method_response: crate::nodes::MethodResponseSender,
|
||||||
|
) {
|
||||||
|
match _method {
|
||||||
|
#run_methods
|
||||||
|
_ => {
|
||||||
|
let _ = _method_response.send(Err(stardust_xr::scenegraph::ScenegraphError::MemberNotFound));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quote!(#opcodes #alias_info #client_side_members #server_side_members #aspect_macro)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_alias_opcodes(aspect: &Aspect, side: Side, _type: MemberType) -> TokenStream {
|
fn generate_alias_opcodes(aspect: &Aspect, side: Side, _type: MemberType) -> TokenStream {
|
||||||
@@ -283,6 +356,7 @@ fn generate_alias_info(aspect: &Aspect) -> TokenStream {
|
|||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
|
#[allow(clippy::all)]
|
||||||
pub static ref #aspect_alias_info_name: crate::nodes::alias::AliasInfo = crate::nodes::alias::AliasInfo {
|
pub static ref #aspect_alias_info_name: crate::nodes::alias::AliasInfo = crate::nodes::alias::AliasInfo {
|
||||||
server_signals: vec![#local_signals],
|
server_signals: vec![#local_signals],
|
||||||
server_methods: vec![#local_methods],
|
server_methods: vec![#local_methods],
|
||||||
@@ -293,8 +367,8 @@ fn generate_alias_info(aspect: &Aspect) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_member(member: &Member) -> TokenStream {
|
fn generate_member(aspect_id: u64, aspect_name: &str, member: &Member) -> TokenStream {
|
||||||
let id = member.opcode;
|
let opcode = member.opcode;
|
||||||
let name = Ident::new(&member.name.to_case(Case::Snake), Span::call_site());
|
let name = Ident::new(&member.name.to_case(Case::Snake), Span::call_site());
|
||||||
let description = &member.description;
|
let description = &member.description;
|
||||||
|
|
||||||
@@ -324,40 +398,53 @@ fn generate_member(member: &Member) -> TokenStream {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r| generate_argument_type(r, false, true))
|
.map(|r| generate_argument_type(r, false, true))
|
||||||
.unwrap_or_else(|| quote!(()));
|
.unwrap_or_else(|| quote!(()));
|
||||||
|
let name_str = name.to_string();
|
||||||
|
|
||||||
match (side, _type) {
|
match (side, _type) {
|
||||||
(Side::Client, MemberType::Method) => {
|
|
||||||
quote! {
|
|
||||||
#[doc = #description]
|
|
||||||
pub async fn #name(#argument_decls) -> color_eyre::eyre::Result<(#return_type, Vec<std::os::fd::OwnedFd>)> {
|
|
||||||
_node.execute_remote_method_typed(#id, &(#argument_uses), vec![]).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Side::Client, MemberType::Signal) => {
|
(Side::Client, MemberType::Signal) => {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #description]
|
#[doc = #description]
|
||||||
pub fn #name(#argument_decls) -> color_eyre::eyre::Result<()> {
|
pub fn #name(#argument_decls) -> crate::core::error::Result<()> {
|
||||||
let serialized = stardust_xr::schemas::flex::serialize((#argument_uses))?;
|
|
||||||
_node.send_remote_signal(#id, serialized)
|
let result = stardust_xr::schemas::flex::serialize((#argument_uses)).map_err(|e|e.into()).and_then(|serialized|_node.send_remote_signal(#aspect_id, #opcode, serialized));
|
||||||
|
if let Err(err) = result.as_ref() {
|
||||||
|
::tracing::warn!("failed to send remote signal: {}::{}, error: {}",#aspect_name,#name_str,err);
|
||||||
|
} else {
|
||||||
|
::tracing::trace!("sent remote signal: {}::{}",#aspect_name,#name_str);
|
||||||
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Side::Server, MemberType::Method) => {
|
(Side::Client, MemberType::Method) => {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #description]
|
#[doc = #description]
|
||||||
fn #name(#argument_decls) -> impl std::future::Future<Output = color_eyre::eyre::Result<#return_type>> + Send + 'static;
|
pub async fn #name(#argument_decls) -> crate::core::error::Result<(#return_type, Vec<std::os::fd::OwnedFd>)> {
|
||||||
|
let result = _node.execute_remote_method_typed(#aspect_id, #opcode, &(#argument_uses), vec![]).await;
|
||||||
|
if let Err(err) = result.as_ref() {
|
||||||
|
::tracing::warn!("failed to call remote method: {}::{}, error: {}",#aspect_name,#name_str,err);
|
||||||
|
} else {
|
||||||
|
::tracing::trace!("called remote method: {}::{}",#aspect_name,#name_str);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Side::Server, MemberType::Signal) => {
|
(Side::Server, MemberType::Signal) => {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #description]
|
#[doc = #description]
|
||||||
fn #name(#argument_decls) -> color_eyre::eyre::Result<()>;
|
fn #name(#argument_decls) -> crate::core::error::Result<()>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Side::Server, MemberType::Method) => {
|
||||||
|
quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
fn #name(#argument_decls) -> impl std::future::Future<Output = crate::core::error::Result<#return_type>> + Send + Sync + 'static;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn generate_handler(member: &Member) -> TokenStream {
|
fn generate_run_member(aspect_name: &Ident, _type: MemberType, member: &Member) -> TokenStream {
|
||||||
let opcode = member.opcode;
|
let opcode = member.opcode;
|
||||||
let member_name_ident = Ident::new(&member.name, Span::call_site());
|
let member_name_ident = Ident::new(&member.name, Span::call_site());
|
||||||
|
|
||||||
@@ -379,7 +466,10 @@ fn generate_handler(member: &Member) -> TokenStream {
|
|||||||
.clone()
|
.clone()
|
||||||
.zip(argument_types)
|
.zip(argument_types)
|
||||||
.map(|(argument_names, argument_types)| {
|
.map(|(argument_names, argument_types)| {
|
||||||
quote!(let (#argument_names): (#argument_types) = stardust_xr::schemas::flex::deserialize(_message.as_ref())?;)
|
quote!{
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
let (#argument_names): (#argument_types) = stardust_xr::schemas::flex::deserialize(_message.as_ref())?;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let serialize = generate_argument_serialize(
|
let serialize = generate_argument_serialize(
|
||||||
@@ -393,21 +483,34 @@ fn generate_handler(member: &Member) -> TokenStream {
|
|||||||
.map(|a| generate_argument_deserialize(&a.name, &a._type, a.optional))
|
.map(|a| generate_argument_deserialize(&a.name, &a._type, a.optional))
|
||||||
.reduce(|a, b| quote!(#a, #b))
|
.reduce(|a, b| quote!(#a, #b))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
match member._type {
|
let member_name = member_name_ident.to_string();
|
||||||
|
let aspect_name_str = aspect_name.to_string();
|
||||||
|
match _type {
|
||||||
MemberType::Signal => quote! {
|
MemberType::Signal => quote! {
|
||||||
node.add_local_signal(#opcode, |_node, _calling_client, _message| {
|
#opcode => { let result = (move || {
|
||||||
#deserialize
|
#deserialize
|
||||||
Self::#member_name_ident(_node, _calling_client.clone(), #argument_uses)
|
<Self as #aspect_name>::#member_name_ident(_node, _calling_client.clone(), #argument_uses)
|
||||||
});
|
})().map_err(|e: crate::core::error::ServerError| stardust_xr::scenegraph::ScenegraphError::MemberError { error: e.to_string() });
|
||||||
|
if let Err(err) = result.as_ref() {
|
||||||
|
::tracing::warn!("failed to receive local signal: {}::{}, error: {}",#aspect_name_str,#member_name,err);
|
||||||
|
} else {
|
||||||
|
::tracing::trace!("received local signal: {}::{}",#aspect_name_str,#member_name);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MemberType::Method => quote! {
|
MemberType::Method => quote! {
|
||||||
node.add_local_method(#opcode, |_node, _calling_client, _message, _method_response| {
|
#opcode => _method_response.wrap_async(async move {
|
||||||
_method_response.wrap_async(async move {
|
|
||||||
#deserialize
|
#deserialize
|
||||||
let result = Self::#member_name_ident(_node, _calling_client.clone(), #argument_uses).await?;
|
let result = <Self as #aspect_name>::#member_name_ident(_node, _calling_client.clone(), #argument_uses).await;
|
||||||
Ok((#serialize, Vec::new()))
|
if let Err(err) = result.as_ref() {
|
||||||
});
|
::tracing::warn!("failed to call local method: {}::{}, error: {}",#aspect_name_str,#member_name,err);
|
||||||
});
|
} else {
|
||||||
|
::tracing::trace!("called local method: {}::{}",#aspect_name_str,#member_name);
|
||||||
|
};
|
||||||
|
let result = result?;
|
||||||
|
Ok((#serialize, Vec::<std::os::fd::OwnedFd>::new()))
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,18 +544,18 @@ fn generate_argument_deserialize(
|
|||||||
}
|
}
|
||||||
if optional {
|
if optional {
|
||||||
let mapping = generate_argument_deserialize("o", argument_type, false);
|
let mapping = generate_argument_deserialize("o", argument_type, false);
|
||||||
return quote!(#name.map(|o| Ok::<_, color_eyre::eyre::Report>(#mapping)).transpose()?);
|
return quote!(#name.map(|o| Ok::<_, crate::core::error::ServerError>(#mapping)).transpose()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
match argument_type {
|
match argument_type {
|
||||||
ArgumentType::Color => quote!(color::rgba_linear!(#name[0], #name[1], #name[2], #name[3])),
|
ArgumentType::Color => quote!(color::rgba_linear!(#name[0], #name[1], #name[2], #name[3])),
|
||||||
ArgumentType::Vec(v) => {
|
ArgumentType::Vec(v) => {
|
||||||
let mapping = generate_argument_deserialize("a", v, false);
|
let mapping = generate_argument_deserialize("a", v, false);
|
||||||
quote!(#name.into_iter().map(|a| Ok(#mapping)).collect::<color_eyre::eyre::Result<Vec<_>>>()?)
|
quote!(#name.into_iter().map(|a| Ok(#mapping)).collect::<crate::core::error::Result<Vec<_>>>()?)
|
||||||
}
|
}
|
||||||
ArgumentType::Map(v) => {
|
ArgumentType::Map(v) => {
|
||||||
let mapping = generate_argument_deserialize("a", v, false);
|
let mapping = generate_argument_deserialize("a", v, false);
|
||||||
quote!(#name.into_iter().map(|(k, a)| Ok((k, #mapping))).collect::<color_eyre::eyre::Result<rustc_hash::FxHashMap<String, _>>>()?)
|
quote!(#name.into_iter().map(|(k, a)| Ok((k, #mapping))).collect::<crate::core::error::Result<rustc_hash::FxHashMap<String, _>>>()?)
|
||||||
}
|
}
|
||||||
_ => quote!(#name),
|
_ => quote!(#name),
|
||||||
}
|
}
|
||||||
@@ -474,11 +577,11 @@ fn generate_argument_serialize(
|
|||||||
ArgumentType::Color => quote!([#name.c.r, #name.c.g, #name.c.b, #name.a]),
|
ArgumentType::Color => quote!([#name.c.r, #name.c.g, #name.c.b, #name.a]),
|
||||||
ArgumentType::Vec(v) => {
|
ArgumentType::Vec(v) => {
|
||||||
let mapping = generate_argument_serialize("a", v, false);
|
let mapping = generate_argument_serialize("a", v, false);
|
||||||
quote!(#name.into_iter().map(|a| Ok(#mapping)).collect::<color_eyre::eyre::Result<Vec<_>>>()?)
|
quote!(#name.into_iter().map(|a| Ok(#mapping)).collect::<crate::core::error::Result<Vec<_>>>()?)
|
||||||
}
|
}
|
||||||
ArgumentType::Map(v) => {
|
ArgumentType::Map(v) => {
|
||||||
let mapping = generate_argument_serialize("a", v, false);
|
let mapping = generate_argument_serialize("a", v, false);
|
||||||
quote!(#name.into_iter().map(|(k, a)| Ok((k, #mapping))).collect::<color_eyre::eyre::Result<rustc_hash::FxHashMap<String, _>>>()?)
|
quote!(#name.into_iter().map(|(k, a)| Ok((k, #mapping))).collect::<crate::core::error::Result<rustc_hash::FxHashMap<String, _>>>()?)
|
||||||
}
|
}
|
||||||
_ => quote!(#name),
|
_ => quote!(#name),
|
||||||
}
|
}
|
||||||
@@ -511,6 +614,7 @@ fn argument_type_option_name(argument_type: &ArgumentType) -> String {
|
|||||||
ArgumentType::Union(u) => u.clone(),
|
ArgumentType::Union(u) => u.clone(),
|
||||||
ArgumentType::Struct(s) => s.clone(),
|
ArgumentType::Struct(s) => s.clone(),
|
||||||
ArgumentType::Node { _type, .. } => _type.clone(),
|
ArgumentType::Node { _type, .. } => _type.clone(),
|
||||||
|
ArgumentType::Fd => "File Descriptor".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn generate_argument_type(
|
fn generate_argument_type(
|
||||||
@@ -607,6 +711,9 @@ fn generate_argument_type(
|
|||||||
quote!(std::sync::Arc<crate::nodes::Node>)
|
quote!(std::sync::Arc<crate::nodes::Node>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ArgumentType::Fd => {
|
||||||
|
quote!(&std::os::fd::OwnedFd)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if optional {
|
if optional {
|
||||||
|
|||||||
135
flake.nix
135
flake.nix
@@ -15,13 +15,21 @@
|
|||||||
flatland.url = "github:StardustXR/flatland";
|
flatland.url = "github:StardustXR/flatland";
|
||||||
};
|
};
|
||||||
outputs =
|
outputs =
|
||||||
inputs@{ self, flake-parts, nixpkgs, hercules-ci-effects, flatland, ... }:
|
inputs@{
|
||||||
|
self,
|
||||||
|
flake-parts,
|
||||||
|
nixpkgs,
|
||||||
|
hercules-ci-effects,
|
||||||
|
flatland,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
name = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
|
name = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
|
||||||
src = builtins.path {
|
src = builtins.path {
|
||||||
name = "${name}-source";
|
name = "${name}-source";
|
||||||
path = toString ./.;
|
path = toString ./.;
|
||||||
filter = path: type:
|
filter =
|
||||||
|
path: type:
|
||||||
nixpkgs.lib.all (n: builtins.baseNameOf path != n) [
|
nixpkgs.lib.all (n: builtins.baseNameOf path != n) [
|
||||||
"flake.nix"
|
"flake.nix"
|
||||||
"flake.lock"
|
"flake.lock"
|
||||||
@@ -29,63 +37,84 @@
|
|||||||
"README.md"
|
"README.md"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
in flake-parts.lib.mkFlake { inherit inputs; } {
|
in
|
||||||
|
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
imports = [ flake-parts.flakeModules.easyOverlay ];
|
imports = [ flake-parts.flakeModules.easyOverlay ];
|
||||||
systems = [ "aarch64-linux" "x86_64-linux" "riscv64-linux" ];
|
systems = [
|
||||||
perSystem = { config, self', inputs', pkgs, system, ... }: {
|
"aarch64-linux"
|
||||||
_module.args.pkgs = import inputs.nixpkgs {
|
"x86_64-linux"
|
||||||
inherit system;
|
"riscv64-linux"
|
||||||
overlays = [ inputs.self.overlays.default ];
|
];
|
||||||
};
|
perSystem =
|
||||||
overlayAttrs = config.packages;
|
{
|
||||||
packages = let sk_gpu = pkgs.callPackage ./nix/sk_gpu.nix { };
|
config,
|
||||||
in {
|
self',
|
||||||
default = self'.packages.${name};
|
inputs',
|
||||||
gnome-graphical-test = self'.checks.gnome-graphical-test;
|
pkgs,
|
||||||
"${name}" = pkgs.callPackage ./nix/stardust-xr-server.nix {
|
system,
|
||||||
inherit name src sk_gpu;
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
_module.args.pkgs = import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [ inputs.self.overlays.default ];
|
||||||
|
};
|
||||||
|
overlayAttrs = config.packages;
|
||||||
|
packages =
|
||||||
|
let
|
||||||
|
sk_gpu = pkgs.callPackage ./nix/sk_gpu.nix { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = self'.packages.${name};
|
||||||
|
gnome-graphical-test = self'.checks.gnome-graphical-test;
|
||||||
|
"${name}" = pkgs.callPackage ./nix/stardust-xr-server.nix {
|
||||||
|
inherit name src sk_gpu;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
apps.default = {
|
||||||
|
type = "app";
|
||||||
|
program = self'.packages.${name} + "/bin/stardust-xr-server";
|
||||||
|
};
|
||||||
|
checks.gnome-graphical-test = pkgs.nixosTest (
|
||||||
|
import ./nix/gnome-graphical-test.nix { inherit pkgs self; }
|
||||||
|
);
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
inputsFrom = [ self'.packages.default ];
|
||||||
|
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
apps.default = {
|
|
||||||
type = "app";
|
|
||||||
program = self'.packages.${name} + "/bin/stardust-xr-server";
|
|
||||||
};
|
|
||||||
checks.gnome-graphical-test = pkgs.nixosTest
|
|
||||||
(import ./nix/gnome-graphical-test.nix { inherit pkgs self; });
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
inputsFrom = [ self'.packages.default ];
|
|
||||||
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
flake = {
|
flake = {
|
||||||
herculesCI.ciSystems = [ "x86_64-linux" ];
|
herculesCI.ciSystems = [ "x86_64-linux" ];
|
||||||
effects = let
|
effects =
|
||||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
let
|
||||||
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||||
in { ref, rev, ... }: {
|
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
||||||
gnome-graphical-test = hci-effects.mkEffect {
|
in
|
||||||
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
{ ref, rev, ... }:
|
||||||
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
{
|
||||||
effectScript = ''
|
gnome-graphical-test = hci-effects.mkEffect {
|
||||||
readSecretString stardustxrDiscord .webhook > .webhook
|
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
||||||
readSecretString stardustxrIpfs .basicauth > .basicauth
|
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
||||||
set -x
|
effectScript = ''
|
||||||
export RESPONSE=$(curl -H @.basicauth -F file=@${
|
readSecretString stardustxrDiscord .webhook > .webhook
|
||||||
self.packages."x86_64-linux".gnome-graphical-test
|
readSecretString stardustxrIpfs .basicauth > .basicauth
|
||||||
}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
|
set -x
|
||||||
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
|
export RESPONSE=$(curl -H @.basicauth -F file=@${
|
||||||
set +x
|
self.packages."x86_64-linux".gnome-graphical-test
|
||||||
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
|
||||||
${pkgs.discord-sh}/bin/discord.sh \
|
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
|
||||||
--description "\`stardustxr/server\` has been modified, here's how it renders \`weston-cliptest\` on \`flatland\` via \`monado-service\` inside of the \`gnome-graphical-test\`" \
|
set +x
|
||||||
--field "Ref;${ref}" \
|
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
||||||
--field "Commit ID;${rev}" \
|
${pkgs.discord-sh}/bin/discord.sh \
|
||||||
--field "Flatland Revision;${flatland.rev}" \
|
--description "\`stardustxr/server\` has been modified, here's how it renders \`weston-cliptest\` on \`flatland\` via \`monado-service\` inside of the \`gnome-graphical-test\`" \
|
||||||
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
--field "Ref;${ref}" \
|
||||||
--image "$ADDRESS"
|
--field "Commit ID;${rev}" \
|
||||||
'';
|
--field "Flatland Revision;${flatland.rev}" \
|
||||||
|
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
||||||
|
--image "$ADDRESS"
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
12
justfile
Normal file
12
justfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
PREFIX := "usr"
|
||||||
|
BINARY := PREFIX / "bin"
|
||||||
|
DESTDIR := "/"
|
||||||
|
|
||||||
|
build:
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
install:
|
||||||
|
install -Dm755 target/release/stardust-xr-server {{ DESTDIR }}{{ BINARY }}/stardust-xr-server
|
||||||
@@ -1,46 +1,57 @@
|
|||||||
use super::{
|
use super::{
|
||||||
client_state::{ClientStateParsed, CLIENT_STATES},
|
client_state::{CLIENT_STATES, ClientStateParsed},
|
||||||
destroy_queue,
|
destroy_queue,
|
||||||
scenegraph::Scenegraph,
|
scenegraph::Scenegraph,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{registry::OwnedRegistry, task},
|
core::{registry::OwnedRegistry, task},
|
||||||
nodes::{
|
nodes::{
|
||||||
audio, data, drawable, fields, input, items,
|
Node, audio, drawable, fields, input, items,
|
||||||
root::{ClientState, Root},
|
root::{ClientState, Root},
|
||||||
spatial, Node,
|
spatial,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{Result, eyre};
|
||||||
use global_counter::primitive::exact::CounterU32;
|
use global_counter::primitive::exact::CounterU32;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stardust_xr::messenger::{self, MessageSenderHandle};
|
use stardust_xr::messenger::{self, MessageSenderHandle};
|
||||||
use std::{fmt::Debug, fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
use tokio::{net::UnixStream, task::JoinHandle};
|
fmt::Debug,
|
||||||
|
fs,
|
||||||
|
iter::FromIterator,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, OnceLock},
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
use tokio::{net::UnixStream, sync::watch, task::JoinHandle};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CLIENTS: OwnedRegistry<Client> = OwnedRegistry::new();
|
pub static ref CLIENTS: OwnedRegistry<Client> = OwnedRegistry::new();
|
||||||
|
static ref INTERNAL_CLIENT_MESSAGE_TIMES: (watch::Sender<Instant>, watch::Receiver<Instant>) = watch::channel(Instant::now());
|
||||||
pub static ref INTERNAL_CLIENT: Arc<Client> = CLIENTS.add(Client {
|
pub static ref INTERNAL_CLIENT: Arc<Client> = CLIENTS.add(Client {
|
||||||
pid: None,
|
pid: None,
|
||||||
// env: None,
|
// env: None,
|
||||||
exe: None,
|
exe: None,
|
||||||
|
|
||||||
dispatch_join_handle: OnceCell::new(),
|
dispatch_join_handle: OnceLock::new(),
|
||||||
flush_join_handle: OnceCell::new(),
|
flush_join_handle: OnceLock::new(),
|
||||||
disconnect_status: OnceCell::new(),
|
disconnect_status: OnceLock::new(),
|
||||||
|
|
||||||
message_sender_handle: None,
|
|
||||||
id_counter: CounterU32::new(0),
|
id_counter: CounterU32::new(0),
|
||||||
|
message_last_received: INTERNAL_CLIENT_MESSAGE_TIMES.1.clone(),
|
||||||
|
message_sender_handle: None,
|
||||||
scenegraph: Default::default(),
|
scenegraph: Default::default(),
|
||||||
root: OnceCell::new(),
|
root: OnceLock::new(),
|
||||||
base_resource_prefixes: Default::default(),
|
base_resource_prefixes: Default::default(),
|
||||||
state: OnceCell::default(),
|
state: OnceLock::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
pub fn tick_internal_client() {
|
||||||
|
let _ = INTERNAL_CLIENT_MESSAGE_TIMES.0.send(Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_env(pid: i32) -> Result<FxHashMap<String, String>, std::io::Error> {
|
pub fn get_env(pid: i32) -> Result<FxHashMap<String, String>, std::io::Error> {
|
||||||
let env = fs::read_to_string(format!("/proc/{pid}/environ"))?;
|
let env = fs::read_to_string(format!("/proc/{pid}/environ"))?;
|
||||||
@@ -59,16 +70,17 @@ pub struct Client {
|
|||||||
pub pid: Option<i32>,
|
pub pid: Option<i32>,
|
||||||
// env: Option<FxHashMap<String, String>>,
|
// env: Option<FxHashMap<String, String>>,
|
||||||
exe: Option<PathBuf>,
|
exe: Option<PathBuf>,
|
||||||
dispatch_join_handle: OnceCell<JoinHandle<Result<()>>>,
|
dispatch_join_handle: OnceLock<JoinHandle<Result<()>>>,
|
||||||
flush_join_handle: OnceCell<JoinHandle<Result<()>>>,
|
flush_join_handle: OnceLock<JoinHandle<Result<()>>>,
|
||||||
disconnect_status: OnceCell<Result<()>>,
|
disconnect_status: OnceLock<Result<()>>,
|
||||||
|
|
||||||
id_counter: CounterU32,
|
id_counter: CounterU32,
|
||||||
|
message_last_received: watch::Receiver<Instant>,
|
||||||
pub message_sender_handle: Option<MessageSenderHandle>,
|
pub message_sender_handle: Option<MessageSenderHandle>,
|
||||||
pub scenegraph: Arc<Scenegraph>,
|
pub scenegraph: Arc<Scenegraph>,
|
||||||
pub root: OnceCell<Arc<Root>>,
|
pub root: OnceLock<Arc<Root>>,
|
||||||
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
|
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
|
||||||
pub state: OnceCell<ClientState>,
|
pub state: OnceLock<ClientState>,
|
||||||
}
|
}
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
|
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
|
||||||
@@ -90,21 +102,23 @@ impl Client {
|
|||||||
.and_then(state)
|
.and_then(state)
|
||||||
.unwrap_or_else(|| Arc::new(ClientStateParsed::default()));
|
.unwrap_or_else(|| Arc::new(ClientStateParsed::default()));
|
||||||
|
|
||||||
|
let (message_time_tx, message_last_received) = watch::channel(Instant::now());
|
||||||
let client = CLIENTS.add(Client {
|
let client = CLIENTS.add(Client {
|
||||||
pid,
|
pid,
|
||||||
// env,
|
// env,
|
||||||
exe: exe.clone(),
|
exe: exe.clone(),
|
||||||
|
|
||||||
dispatch_join_handle: OnceCell::new(),
|
dispatch_join_handle: OnceLock::new(),
|
||||||
flush_join_handle: OnceCell::new(),
|
flush_join_handle: OnceLock::new(),
|
||||||
disconnect_status: OnceCell::new(),
|
disconnect_status: OnceLock::new(),
|
||||||
|
|
||||||
id_counter: CounterU32::new(256),
|
id_counter: CounterU32::new(256),
|
||||||
|
message_last_received,
|
||||||
message_sender_handle: Some(messenger_tx.handle()),
|
message_sender_handle: Some(messenger_tx.handle()),
|
||||||
scenegraph: scenegraph.clone(),
|
scenegraph: scenegraph.clone(),
|
||||||
root: OnceCell::new(),
|
root: OnceLock::new(),
|
||||||
base_resource_prefixes: Default::default(),
|
base_resource_prefixes: Default::default(),
|
||||||
state: OnceCell::default(),
|
state: OnceLock::default(),
|
||||||
});
|
});
|
||||||
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
|
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
|
||||||
let _ = client.root.set(Root::create(&client, state.root)?);
|
let _ = client.root.set(Root::create(&client, state.root)?);
|
||||||
@@ -112,7 +126,6 @@ impl Client {
|
|||||||
fields::create_interface(&client)?;
|
fields::create_interface(&client)?;
|
||||||
drawable::create_interface(&client)?;
|
drawable::create_interface(&client)?;
|
||||||
audio::create_interface(&client)?;
|
audio::create_interface(&client)?;
|
||||||
data::create_interface(&client)?;
|
|
||||||
input::create_interface(&client)?;
|
input::create_interface(&client)?;
|
||||||
items::camera::create_interface(&client)?;
|
items::camera::create_interface(&client)?;
|
||||||
items::panel::create_interface(&client)?;
|
items::panel::create_interface(&client)?;
|
||||||
@@ -129,7 +142,7 @@ impl Client {
|
|||||||
.map(|exe| exe.to_string())
|
.map(|exe| exe.to_string())
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "??".to_string());
|
.unwrap_or_else(|| "??".to_string());
|
||||||
let _ = client.dispatch_join_handle.get_or_try_init(|| {
|
let _ = client.dispatch_join_handle.get_or_init(|| {
|
||||||
task::new(
|
task::new(
|
||||||
|| {
|
|| {
|
||||||
format!(
|
format!(
|
||||||
@@ -144,12 +157,14 @@ impl Client {
|
|||||||
if let Err(e) = messenger_rx.dispatch(&*scenegraph).await {
|
if let Err(e) = messenger_rx.dispatch(&*scenegraph).await {
|
||||||
client.disconnect(Err(e.into()));
|
client.disconnect(Err(e.into()));
|
||||||
}
|
}
|
||||||
|
let _ = message_time_tx.send(Instant::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.unwrap()
|
||||||
});
|
});
|
||||||
let _ = client.flush_join_handle.get_or_try_init(|| {
|
let _ = client.flush_join_handle.get_or_init(|| {
|
||||||
task::new(
|
task::new(
|
||||||
|| format!("client flush pid={} exe={}", &pid_printable, &exe_printable,),
|
|| format!("client flush pid={} exe={}", &pid_printable, &exe_printable,),
|
||||||
{
|
{
|
||||||
@@ -163,6 +178,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(client)
|
Ok(client)
|
||||||
@@ -200,6 +216,11 @@ impl Client {
|
|||||||
.ok_or_else(|| eyre!("{} not found", name))
|
.ok_or_else(|| eyre!("{} not found", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unresponsive(&self) -> bool {
|
||||||
|
let time_since_last_message = self.message_last_received.borrow().elapsed();
|
||||||
|
time_since_last_message.as_millis() > 500
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disconnect(&self, reason: Result<()>) {
|
pub fn disconnect(&self, reason: Result<()>) {
|
||||||
let _ = self.disconnect_status.set(reason);
|
let _ = self.disconnect_status.set(reason);
|
||||||
if let Some(dispatch_join_handle) = self.dispatch_join_handle.get() {
|
if let Some(dispatch_join_handle) = self.dispatch_join_handle.get() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use super::client::{get_env, Client};
|
use super::client::{Client, get_env};
|
||||||
use crate::nodes::{root::ClientState, spatial::Spatial, Node};
|
use crate::nodes::{Node, root::ClientState, spatial::Spatial};
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
@@ -69,7 +69,7 @@ impl ClientStateParsed {
|
|||||||
let app_name = self
|
let app_name = self
|
||||||
.launch_info
|
.launch_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|l| l.cmdline.first().unwrap().split('/').last().unwrap())
|
.map(|l| l.cmdline.first().unwrap().split('/').next_back().unwrap())
|
||||||
.unwrap_or("unknown");
|
.unwrap_or("unknown");
|
||||||
let state_file_path = directory
|
let state_file_path = directory
|
||||||
.join(format!("{app_name}-{}", nanoid::nanoid!()))
|
.join(format!("{app_name}-{}", nanoid::nanoid!()))
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
use once_cell::sync::Lazy;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::any::Any;
|
use std::{any::Any, sync::LazyLock};
|
||||||
use tokio::sync::mpsc::{self, unbounded_channel};
|
use tokio::sync::mpsc::{self, unbounded_channel};
|
||||||
|
|
||||||
type Anything = Box<dyn Any + Send + Sync>;
|
type Anything = Box<dyn Any + Send + Sync>;
|
||||||
|
|
||||||
static MAIN_DESTROY_QUEUE: Lazy<(
|
static MAIN_DESTROY_QUEUE: LazyLock<(
|
||||||
mpsc::UnboundedSender<Anything>,
|
mpsc::UnboundedSender<Anything>,
|
||||||
Mutex<mpsc::UnboundedReceiver<Anything>>,
|
Mutex<mpsc::UnboundedReceiver<Anything>>,
|
||||||
)> = Lazy::new(|| {
|
)> = LazyLock::new(|| {
|
||||||
let (tx, rx) = unbounded_channel();
|
let (tx, rx) = unbounded_channel();
|
||||||
(tx, Mutex::new(rx))
|
(tx, Mutex::new(rx))
|
||||||
});
|
});
|
||||||
|
|||||||
75
src/core/error.rs
Normal file
75
src/core/error.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use std::any::TypeId;
|
||||||
|
|
||||||
|
use color_eyre::eyre::Report;
|
||||||
|
use stardust_xr::{
|
||||||
|
messenger::MessengerError,
|
||||||
|
schemas::flex::{
|
||||||
|
FlexSerializeError,
|
||||||
|
flexbuffers::{DeserializationError, ReaderError},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use stereokit_rust::StereoKitError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T, E = ServerError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ServerError {
|
||||||
|
#[error("Internal: Unable to get client")]
|
||||||
|
NoClient,
|
||||||
|
#[error("Messenger does not exist for this node")]
|
||||||
|
NoMessenger,
|
||||||
|
#[error("Messenger error: {0}")]
|
||||||
|
MessengerError(#[from] MessengerError),
|
||||||
|
#[error("Remote method error: {0}")]
|
||||||
|
RemoteMethodError(String),
|
||||||
|
#[error("Serialization error: {0}")]
|
||||||
|
SerializationError(#[from] FlexSerializeError),
|
||||||
|
#[error("Deserialization error: {0}")]
|
||||||
|
DeserializationError(#[from] DeserializationError),
|
||||||
|
#[error("Reader error: {0}")]
|
||||||
|
ReaderError(#[from] ReaderError),
|
||||||
|
#[error("StereoKit error: {0}")]
|
||||||
|
StereoKitError(#[from] StereoKitError),
|
||||||
|
#[error("Aspect {} does not exist for node", 0.to_string())]
|
||||||
|
NoAspect(TypeId),
|
||||||
|
#[error("{0}")]
|
||||||
|
Report(#[from] Report),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bail {
|
||||||
|
($msg:literal $(,)?) => {
|
||||||
|
return Err($crate::core::error::ServerError::from(color_eyre::eyre::eyre!($msg)));
|
||||||
|
};
|
||||||
|
($err:expr $(,)?) => {
|
||||||
|
return Err($crate::core::error::ServerError::from(color_eyre::eyre::eyre!($err)));
|
||||||
|
};
|
||||||
|
($fmt:expr, $($arg:tt)*) => {
|
||||||
|
return Err($crate::core::error::ServerError::from(color_eyre::eyre::eyre!($fmt, $($arg)*)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ensure {
|
||||||
|
($cond:expr $(,)?) => {
|
||||||
|
if !$cond {
|
||||||
|
$crate::ensure!($cond, concat!("Condition failed: `", stringify!($cond), "`"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($cond:expr, $msg:literal $(,)?) => {
|
||||||
|
if !$cond {
|
||||||
|
return Err($crate::core::error::ServerError::from(color_eyre::eyre::eyre!($msg)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($cond:expr, $err:expr $(,)?) => {
|
||||||
|
if !$cond {
|
||||||
|
return Err($crate::core::error::ServerError::from(color_eyre::eyre::eyre!($err)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($cond:expr, $fmt:expr, $($arg:tt)*) => {
|
||||||
|
if !$cond {
|
||||||
|
return Err($crate::core::error::ServerError::from(color_eyre::eyre::eyre!($fmt, $($arg)*)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#[macro_export]
|
|
||||||
macro_rules! create_interface {
|
|
||||||
($iface:ident) => {
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|
||||||
let node = Node::from_id(client, INTERFACE_NODE_ID, false);
|
|
||||||
<$iface as self::InterfaceAspect>::add_node_members(&node);
|
|
||||||
node.add_to_scenegraph()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ pub mod client;
|
|||||||
pub mod client_state;
|
pub mod client_state;
|
||||||
pub mod delta;
|
pub mod delta;
|
||||||
pub mod destroy_queue;
|
pub mod destroy_queue;
|
||||||
pub mod idl_utils;
|
pub mod error;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
pub mod scenegraph;
|
pub mod scenegraph;
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use parking_lot::{const_mutex, MappedMutexGuard, Mutex, MutexGuard};
|
use dashmap::DashMap;
|
||||||
|
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard, const_mutex};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, LazyLock, Weak};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Registry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Weak<T>>>>);
|
pub struct Registry<T: Send + Sync + ?Sized>(MaybeLazy<DashMap<usize, Weak<T>>>);
|
||||||
|
|
||||||
impl<T: Send + Sync + ?Sized> Registry<T> {
|
impl<T: Send + Sync + ?Sized> Registry<T> {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Registry(const_mutex(None))
|
Registry(MaybeLazy::Lazy(LazyLock::new(DashMap::default)))
|
||||||
}
|
|
||||||
fn lock(&self) -> MappedMutexGuard<FxHashMap<usize, Weak<T>>> {
|
|
||||||
MutexGuard::map(self.0.lock(), |r| r.get_or_insert_with(FxHashMap::default))
|
|
||||||
}
|
}
|
||||||
pub fn add(&self, t: T) -> Arc<T>
|
pub fn add(&self, t: T) -> Arc<T>
|
||||||
where
|
where
|
||||||
@@ -24,30 +23,29 @@ impl<T: Send + Sync + ?Sized> Registry<T> {
|
|||||||
t_arc
|
t_arc
|
||||||
}
|
}
|
||||||
pub fn add_raw(&self, t: &Arc<T>) {
|
pub fn add_raw(&self, t: &Arc<T>) {
|
||||||
self.lock()
|
self.0
|
||||||
.insert(Arc::as_ptr(t) as *const () as usize, Arc::downgrade(t));
|
.insert(Arc::as_ptr(t) as *const () as usize, Arc::downgrade(t));
|
||||||
}
|
}
|
||||||
pub fn contains(&self, t: &T) -> bool {
|
pub fn contains(&self, t: &T) -> bool {
|
||||||
self.lock()
|
self.0
|
||||||
.contains_key(&(ptr::addr_of!(*t) as *const () as usize))
|
.contains_key(&(ptr::addr_of!(*t) as *const () as usize))
|
||||||
}
|
}
|
||||||
pub fn get_changes(old: &Registry<T>, new: &Registry<T>) -> (Vec<Arc<T>>, Vec<Arc<T>>) {
|
pub fn get_changes(old: &Registry<T>, new: &Registry<T>) -> (Vec<Arc<T>>, Vec<Arc<T>>) {
|
||||||
let old = old.lock();
|
|
||||||
let new = new.lock();
|
|
||||||
|
|
||||||
let mut added = Vec::new();
|
let mut added = Vec::new();
|
||||||
let mut removed = Vec::new();
|
let mut removed = Vec::new();
|
||||||
|
|
||||||
for (id, entry) in new.iter() {
|
for pair in new.0.iter() {
|
||||||
|
let (id, entry) = pair.pair();
|
||||||
if let Some(entry) = entry.upgrade() {
|
if let Some(entry) = entry.upgrade() {
|
||||||
if !old.contains_key(id) {
|
if !old.0.contains_key(id) {
|
||||||
added.push(entry);
|
added.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (id, entry) in old.iter() {
|
for pair in old.0.iter() {
|
||||||
|
let (id, entry) = pair.pair();
|
||||||
if let Some(entry) = entry.upgrade() {
|
if let Some(entry) = entry.upgrade() {
|
||||||
if !new.contains_key(id) {
|
if !new.0.contains_key(id) {
|
||||||
removed.push(entry);
|
removed.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,52 +53,48 @@ impl<T: Send + Sync + ?Sized> Registry<T> {
|
|||||||
(added, removed)
|
(added, removed)
|
||||||
}
|
}
|
||||||
pub fn get_valid_contents(&self) -> Vec<Arc<T>> {
|
pub fn get_valid_contents(&self) -> Vec<Arc<T>> {
|
||||||
self.lock()
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|pair| pair.1.upgrade())
|
.filter_map(|pair| pair.value().upgrade())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
pub fn set(&self, other: &Registry<T>) {
|
pub fn set(&self, other: &Registry<T>) {
|
||||||
self.lock().clone_from(&other.lock());
|
self.clear();
|
||||||
|
for (key, value) in other.0.deref().clone().into_iter() {
|
||||||
|
self.0.insert(key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn take_valid_contents(&self) -> Vec<Arc<T>> {
|
pub fn take_valid_contents(&self) -> Vec<Arc<T>> {
|
||||||
self.0
|
let contents = self.get_valid_contents();
|
||||||
.lock()
|
self.0.clear();
|
||||||
.take()
|
contents
|
||||||
.unwrap_or_default()
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|pair| pair.1.upgrade())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
pub fn retain<F: Fn(&Arc<T>) -> bool>(&self, f: F) {
|
pub fn retain<F: Fn(&Arc<T>) -> bool>(&self, f: F) {
|
||||||
self.lock().retain(|_, v| {
|
self.0.retain(|_, v| {
|
||||||
let Some(v) = v.upgrade() else {
|
let Some(v) = v.upgrade() else {
|
||||||
|
// why would we want to retain things we can't upgrade?
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
(f)(&v)
|
(f)(&v)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn remove(&self, t: &T) {
|
pub fn remove(&self, t: &T) {
|
||||||
self.lock()
|
self.0.remove(&(ptr::addr_of!(*t) as *const () as usize));
|
||||||
.remove(&(ptr::addr_of!(*t) as *const () as usize));
|
|
||||||
}
|
}
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.lock().clear();
|
self.0.clear();
|
||||||
}
|
}
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
let registry = self.0.lock();
|
if self.0.is_empty() {
|
||||||
let Some(registry) = &*registry else {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if registry.is_empty() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
registry.values().all(|v| v.strong_count() == 0)
|
self.0.iter().all(|v| v.value().strong_count() == 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send + Sync + ?Sized> Clone for Registry<T> {
|
impl<T: Send + Sync + ?Sized> Clone for Registry<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self(Mutex::new(self.0.lock().clone()))
|
Self(self.0.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Send + Sync + ?Sized> Default for Registry<T> {
|
impl<T: Send + Sync + ?Sized> Default for Registry<T> {
|
||||||
@@ -109,6 +103,40 @@ impl<T: Send + Sync + ?Sized> Default for Registry<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync + Sized> FromIterator<Arc<T>> for Registry<T> {
|
||||||
|
fn from_iter<I: IntoIterator<Item = Arc<T>>>(iter: I) -> Self {
|
||||||
|
Registry(MaybeLazy::NonLazy(
|
||||||
|
iter.into_iter()
|
||||||
|
.map(|i| (Arc::as_ptr(&i) as usize, Arc::downgrade(&i)))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum MaybeLazy<T> {
|
||||||
|
Lazy(LazyLock<T>),
|
||||||
|
NonLazy(T),
|
||||||
|
}
|
||||||
|
impl<T: Clone> Clone for MaybeLazy<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
MaybeLazy::Lazy(lazy_lock) => Self::NonLazy(lazy_lock.deref().clone()),
|
||||||
|
MaybeLazy::NonLazy(v) => Self::NonLazy(v.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Deref for MaybeLazy<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
MaybeLazy::Lazy(lazy_lock) => lazy_lock,
|
||||||
|
MaybeLazy::NonLazy(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct OwnedRegistry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Arc<T>>>>);
|
pub struct OwnedRegistry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Arc<T>>>>);
|
||||||
|
|
||||||
impl<T: Send + Sync + ?Sized> OwnedRegistry<T> {
|
impl<T: Send + Sync + ?Sized> OwnedRegistry<T> {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use crate::nodes::alias::get_original;
|
use crate::core::error::Result;
|
||||||
use crate::nodes::Node;
|
use crate::nodes::Node;
|
||||||
|
use crate::nodes::alias::get_original;
|
||||||
use crate::{core::client::Client, nodes::Message};
|
use crate::{core::client::Client, nodes::Message};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -11,13 +10,13 @@ use stardust_xr::scenegraph::ScenegraphError;
|
|||||||
use stardust_xr::schemas::flex::serialize;
|
use stardust_xr::schemas::flex::serialize;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, OnceLock, Weak};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use tracing::{debug, debug_span};
|
use tracing::{debug, debug_span};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Scenegraph {
|
pub struct Scenegraph {
|
||||||
pub(super) client: OnceCell<Weak<Client>>,
|
pub(super) client: OnceLock<Weak<Client>>,
|
||||||
nodes: Mutex<FxHashMap<u64, Arc<Node>>>,
|
nodes: Mutex<FxHashMap<u64, Arc<Node>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,26 +58,26 @@ impl MethodResponseSender {
|
|||||||
// ) {
|
// ) {
|
||||||
// let _ = self.0.send(map_method_return(result));
|
// let _ = self.0.send(map_method_return(result));
|
||||||
// }
|
// }
|
||||||
pub fn wrap_sync<F: FnOnce() -> color_eyre::eyre::Result<Message>>(self, f: F) {
|
pub fn wrap_sync<F: FnOnce() -> crate::core::error::Result<Message>>(self, f: F) {
|
||||||
self.send(f().map_err(|e| ScenegraphError::MethodError {
|
self.send(f().map_err(|e| ScenegraphError::MemberError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
pub fn wrap_async<T: Serialize>(
|
pub fn wrap_async<T: Serialize>(
|
||||||
self,
|
self,
|
||||||
f: impl Future<Output = color_eyre::eyre::Result<(T, Vec<OwnedFd>)>> + Send + 'static,
|
f: impl Future<Output = Result<(T, Vec<OwnedFd>)>> + Send + 'static,
|
||||||
) {
|
) {
|
||||||
tokio::task::spawn(async move { self.0.send(map_method_return(f.await)) });
|
tokio::task::spawn(async move { self.0.send(map_method_return(f.await)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn map_method_return<T: Serialize>(
|
fn map_method_return<T: Serialize>(
|
||||||
result: color_eyre::eyre::Result<(T, Vec<OwnedFd>)>,
|
result: Result<(T, Vec<OwnedFd>)>,
|
||||||
) -> Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError> {
|
) -> Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError> {
|
||||||
let (value, fds) = result.map_err(|e| ScenegraphError::MethodError {
|
let (value, fds) = result.map_err(|e| ScenegraphError::MemberError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let serialized_value = serialize(value).map_err(|e| ScenegraphError::MethodError {
|
let serialized_value = serialize(value).map_err(|e| ScenegraphError::MemberError {
|
||||||
error: format!("Internal: Serialization failed: {e}"),
|
error: format!("Internal: Serialization failed: {e}"),
|
||||||
})?;
|
})?;
|
||||||
Ok((serialized_value, fds))
|
Ok((serialized_value, fds))
|
||||||
@@ -86,19 +85,21 @@ fn map_method_return<T: Serialize>(
|
|||||||
impl scenegraph::Scenegraph for Scenegraph {
|
impl scenegraph::Scenegraph for Scenegraph {
|
||||||
fn send_signal(
|
fn send_signal(
|
||||||
&self,
|
&self,
|
||||||
node: u64,
|
node_id: u64,
|
||||||
|
aspect_id: u64,
|
||||||
method: u64,
|
method: u64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
fds: Vec<OwnedFd>,
|
fds: Vec<OwnedFd>,
|
||||||
) -> Result<(), ScenegraphError> {
|
) -> Result<(), ScenegraphError> {
|
||||||
let Some(client) = self.get_client() else {
|
let Some(client) = self.get_client() else {
|
||||||
return Err(ScenegraphError::SignalNotFound);
|
return Err(ScenegraphError::NodeNotFound);
|
||||||
};
|
};
|
||||||
debug_span!("Handle signal", node, method).in_scope(|| {
|
debug_span!("Handle signal", aspect_id, node_id, method).in_scope(|| {
|
||||||
self.get_node(node)
|
self.get_node(node_id)
|
||||||
.ok_or(ScenegraphError::NodeNotFound)?
|
.ok_or(ScenegraphError::NodeNotFound)?
|
||||||
.send_local_signal(
|
.send_local_signal(
|
||||||
client,
|
client,
|
||||||
|
aspect_id,
|
||||||
method,
|
method,
|
||||||
Message {
|
Message {
|
||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
@@ -109,23 +110,25 @@ impl scenegraph::Scenegraph for Scenegraph {
|
|||||||
}
|
}
|
||||||
fn execute_method(
|
fn execute_method(
|
||||||
&self,
|
&self,
|
||||||
node: u64,
|
node_id: u64,
|
||||||
|
aspect_id: u64,
|
||||||
method: u64,
|
method: u64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
fds: Vec<OwnedFd>,
|
fds: Vec<OwnedFd>,
|
||||||
response: oneshot::Sender<Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError>>,
|
response: oneshot::Sender<Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError>>,
|
||||||
) {
|
) {
|
||||||
let Some(client) = self.get_client() else {
|
let Some(client) = self.get_client() else {
|
||||||
let _ = response.send(Err(ScenegraphError::MethodNotFound));
|
let _ = response.send(Err(ScenegraphError::NodeNotFound));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
debug!(node, method, "Handle method");
|
debug!(aspect_id, node_id, method, "Handle method");
|
||||||
let Some(node) = self.get_node(node) else {
|
let Some(node) = self.get_node(node_id) else {
|
||||||
let _ = response.send(Err(ScenegraphError::NodeNotFound));
|
let _ = response.send(Err(ScenegraphError::NodeNotFound));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
node.execute_local_method(
|
node.execute_local_method(
|
||||||
client,
|
client,
|
||||||
|
aspect_id,
|
||||||
method,
|
method,
|
||||||
Message {
|
Message {
|
||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
|
|||||||
107
src/main.rs
107
src/main.rs
@@ -11,30 +11,32 @@ use crate::nodes::items::camera;
|
|||||||
use crate::nodes::{audio, drawable, input};
|
use crate::nodes::{audio, drawable, input};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use core::client::Client;
|
use core::client::{Client, tick_internal_client};
|
||||||
use core::task;
|
use core::task;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use objects::ServerObjects;
|
use objects::ServerObjects;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use session::{launch_start, save_session};
|
use session::{launch_start, save_session};
|
||||||
|
use stardust_xr::schemas::dbus::object_registry::ObjectRegistry;
|
||||||
use stardust_xr::server;
|
use stardust_xr::server;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, OnceLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use stereokit_rust::material::Material;
|
use stereokit_rust::material::Material;
|
||||||
use stereokit_rust::shader::Shader;
|
use stereokit_rust::shader::Shader;
|
||||||
use stereokit_rust::sk::{sk_quit, AppMode, DepthMode, OriginMode, QuitReason, SkSettings};
|
use stereokit_rust::sk::{
|
||||||
use stereokit_rust::system::{LogLevel, Renderer};
|
AppMode, DepthMode, DisplayBlend, OriginMode, QuitReason, SkSettings, sk_quit,
|
||||||
|
};
|
||||||
|
use stereokit_rust::system::{Handed, Input, LogLevel, Renderer};
|
||||||
use stereokit_rust::tex::{SHCubemap, Tex, TexFormat, TexType};
|
use stereokit_rust::tex::{SHCubemap, Tex, TexFormat, TexType};
|
||||||
use stereokit_rust::ui::Ui;
|
use stereokit_rust::ui::Ui;
|
||||||
use stereokit_rust::util::{Color128, Time};
|
use stereokit_rust::util::{Color128, SphericalHarmonics, Time};
|
||||||
use tokio::net::UnixListener;
|
use tokio::net::UnixListener;
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
use tracing::metadata::LevelFilter;
|
use tracing::metadata::LevelFilter;
|
||||||
use tracing::{debug_span, error, info};
|
use tracing::{debug_span, error, info};
|
||||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
||||||
use zbus::fdo::ObjectManager;
|
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
use zbus::fdo::ObjectManager;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
@@ -65,12 +67,16 @@ struct CliArgs {
|
|||||||
/// Restore the session with the given ID (or `latest`), ignoring the startup script. Sessions are stored in directories at `~/.local/state/stardust/`.
|
/// Restore the session with the given ID (or `latest`), ignoring the startup script. Sessions are stored in directories at `~/.local/state/stardust/`.
|
||||||
#[clap(id = "SESSION_ID", long = "restore", action)]
|
#[clap(id = "SESSION_ID", long = "restore", action)]
|
||||||
restore: Option<String>,
|
restore: Option<String>,
|
||||||
|
/// this should fix nvidia issues, it'll only help on driver 565+
|
||||||
|
/// and only if running under wayland, probably
|
||||||
|
#[clap(long)]
|
||||||
|
nvidia: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
|
static STARDUST_INSTANCE: OnceLock<String> = OnceLock::new();
|
||||||
|
|
||||||
// #[tokio::main]
|
// #[tokio::main(flavor = "current_thread")]
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
color_eyre::install().unwrap();
|
color_eyre::install().unwrap();
|
||||||
|
|
||||||
@@ -91,11 +97,29 @@ async fn main() {
|
|||||||
.with_thread_names(true)
|
.with_thread_names(true)
|
||||||
.with_ansi(true)
|
.with_ansi(true)
|
||||||
.with_line_number(true)
|
.with_line_number(true)
|
||||||
.with_filter(EnvFilter::from_default_env());
|
.with_filter(
|
||||||
|
EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::WARN.into())
|
||||||
|
.from_env_lossy(),
|
||||||
|
);
|
||||||
registry.with(log_layer).init();
|
registry.with(log_layer).init();
|
||||||
|
|
||||||
let cli_args = CliArgs::parse();
|
let cli_args = CliArgs::parse();
|
||||||
|
|
||||||
|
if cli_args.nvidia && !cli_args.flatscreen {
|
||||||
|
// Only call this while singlethreaded since it can/will cause raceconditions with other
|
||||||
|
// functions reading or writing from the env
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var("__GLX_VENDOR_LIBRARY_NAME", "mesa");
|
||||||
|
std::env::set_var(
|
||||||
|
"__EGL_VENDOR_LIBRARY_FILENAMES",
|
||||||
|
"/usr/share/glvnd/egl_vendor.d/50_mesa.json",
|
||||||
|
);
|
||||||
|
std::env::set_var("MESA_LOADER_DRIVER_OVERRIDE", "zink");
|
||||||
|
std::env::set_var("GALLIUM_DRIVER", "zink");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let socket_path =
|
let socket_path =
|
||||||
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
||||||
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
||||||
@@ -120,7 +144,9 @@ async fn main() {
|
|||||||
|
|
||||||
let project_dirs = ProjectDirs::from("", "", "stardust");
|
let project_dirs = ProjectDirs::from("", "", "stardust");
|
||||||
if project_dirs.is_none() {
|
if project_dirs.is_none() {
|
||||||
error!("Unable to get Stardust project directories, default skybox and startup script will not work.");
|
error!(
|
||||||
|
"Unable to get Stardust project directories, default skybox and startup script will not work."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbus_connection = Connection::session()
|
let dbus_connection = Connection::session()
|
||||||
@@ -129,7 +155,9 @@ async fn main() {
|
|||||||
dbus_connection
|
dbus_connection
|
||||||
.request_name("org.stardustxr.HMD")
|
.request_name("org.stardustxr.HMD")
|
||||||
.await
|
.await
|
||||||
.expect("Another instance of the server is running. This is not supported currently (but is planned).");
|
.expect(
|
||||||
|
"Another instance of the server is running. This is not supported currently (but is planned).",
|
||||||
|
);
|
||||||
|
|
||||||
dbus_connection
|
dbus_connection
|
||||||
.object_server()
|
.object_server()
|
||||||
@@ -137,13 +165,25 @@ async fn main() {
|
|||||||
.await
|
.await
|
||||||
.expect("Couldn't add the object manager");
|
.expect("Couldn't add the object manager");
|
||||||
|
|
||||||
|
let object_registry = ObjectRegistry::new(&dbus_connection).await.expect(
|
||||||
|
"Couldn't make the object registry to find all objects with given interfaces in d-bus",
|
||||||
|
);
|
||||||
|
|
||||||
let sk_ready_notifier = Arc::new(Notify::new());
|
let sk_ready_notifier = Arc::new(Notify::new());
|
||||||
let stereokit_loop = tokio::task::spawn_blocking({
|
let stereokit_loop = tokio::task::spawn_blocking({
|
||||||
let sk_ready_notifier = sk_ready_notifier.clone();
|
let sk_ready_notifier = sk_ready_notifier.clone();
|
||||||
let project_dirs = project_dirs.clone();
|
let project_dirs = project_dirs.clone();
|
||||||
let cli_args = cli_args.clone();
|
let cli_args = cli_args.clone();
|
||||||
let dbus_connection = dbus_connection.clone();
|
let dbus_connection = dbus_connection.clone();
|
||||||
move || stereokit_loop(sk_ready_notifier, project_dirs, cli_args, dbus_connection)
|
move || {
|
||||||
|
stereokit_loop(
|
||||||
|
sk_ready_notifier,
|
||||||
|
project_dirs,
|
||||||
|
cli_args,
|
||||||
|
dbus_connection,
|
||||||
|
object_registry,
|
||||||
|
)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
sk_ready_notifier.notified().await;
|
sk_ready_notifier.notified().await;
|
||||||
let mut startup_children = project_dirs
|
let mut startup_children = project_dirs
|
||||||
@@ -166,14 +206,19 @@ async fn main() {
|
|||||||
info!("Cleanly shut down Stardust");
|
info!("Cleanly shut down Stardust");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DEFAULT_SKYTEX: OnceLock<Tex> = OnceLock::new();
|
||||||
|
static DEFAULT_SKYLIGHT: OnceLock<SphericalHarmonics> = OnceLock::new();
|
||||||
|
|
||||||
fn stereokit_loop(
|
fn stereokit_loop(
|
||||||
sk_ready_notifier: Arc<Notify>,
|
sk_ready_notifier: Arc<Notify>,
|
||||||
project_dirs: Option<ProjectDirs>,
|
project_dirs: Option<ProjectDirs>,
|
||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
dbus_connection: Connection,
|
dbus_connection: Connection,
|
||||||
|
object_registry: ObjectRegistry,
|
||||||
) {
|
) {
|
||||||
let sk = SkSettings::default()
|
let sk = SkSettings::default()
|
||||||
.app_name("Stardust XR")
|
.app_name("Stardust XR")
|
||||||
|
.blend_preference(DisplayBlend::AnyTransparent)
|
||||||
.mode(if args.flatscreen {
|
.mode(if args.flatscreen {
|
||||||
AppMode::Simulator
|
AppMode::Simulator
|
||||||
} else {
|
} else {
|
||||||
@@ -201,23 +246,33 @@ fn stereokit_loop(
|
|||||||
Material::default().shader(Shader::pbr_clip());
|
Material::default().shader(Shader::pbr_clip());
|
||||||
Ui::enable_far_interact(false);
|
Ui::enable_far_interact(false);
|
||||||
|
|
||||||
|
let left_hand_material = Material::find("default/material_hand").unwrap();
|
||||||
|
let mut right_hand_material = left_hand_material.copy();
|
||||||
|
right_hand_material.id("right_hand");
|
||||||
|
Input::hand_material(Handed::Right, Some(Material::find("right_hand").unwrap()));
|
||||||
|
|
||||||
|
Input::hand_visible(Handed::Left, false);
|
||||||
|
Input::hand_visible(Handed::Right, false);
|
||||||
|
|
||||||
// Skytex/light stuff
|
// Skytex/light stuff
|
||||||
{
|
{
|
||||||
|
let _ = DEFAULT_SKYTEX.set(Tex::gen_color(
|
||||||
|
Color128::BLACK,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
TexType::Cubemap,
|
||||||
|
TexFormat::RGBA32,
|
||||||
|
));
|
||||||
|
let _ = DEFAULT_SKYLIGHT.set(Renderer::get_skylight());
|
||||||
if let Some(sky) = project_dirs
|
if let Some(sky) = project_dirs
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|dirs| dirs.config_dir().join("skytex.hdr"))
|
.map(|dirs| dirs.config_dir().join("skytex.hdr"))
|
||||||
.filter(|f| f.exists())
|
.filter(|f| f.exists())
|
||||||
.and_then(|p| SHCubemap::from_cubemap_equirectangular(p, true, 100).ok())
|
.and_then(|p| SHCubemap::from_cubemap(p, true, 100).ok())
|
||||||
{
|
{
|
||||||
sky.render_as_sky();
|
sky.render_as_sky();
|
||||||
} else {
|
} else {
|
||||||
Renderer::skytex(Tex::gen_color(
|
Renderer::skytex(DEFAULT_SKYTEX.get().unwrap());
|
||||||
Color128::BLACK,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
TexType::Cubemap,
|
|
||||||
TexFormat::RGBA32,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +286,7 @@ fn stereokit_loop(
|
|||||||
let mut objects = ServerObjects::new(
|
let mut objects = ServerObjects::new(
|
||||||
dbus_connection.clone(),
|
dbus_connection.clone(),
|
||||||
&sk,
|
&sk,
|
||||||
|
[left_hand_material, right_hand_material],
|
||||||
args.disable_controllers,
|
args.disable_controllers,
|
||||||
args.disable_hands,
|
args.disable_hands,
|
||||||
);
|
);
|
||||||
@@ -246,7 +302,7 @@ fn stereokit_loop(
|
|||||||
wayland.frame_event();
|
wayland.frame_event();
|
||||||
destroy_queue::clear();
|
destroy_queue::clear();
|
||||||
|
|
||||||
objects.update(&sk, token);
|
objects.update(&sk, token, &dbus_connection, &object_registry);
|
||||||
input::process_input();
|
input::process_input();
|
||||||
nodes::root::Root::send_frame_events(Time::get_step_unscaled());
|
nodes::root::Root::send_frame_events(Time::get_step_unscaled());
|
||||||
adaptive_sleep(
|
adaptive_sleep(
|
||||||
@@ -255,6 +311,7 @@ fn stereokit_loop(
|
|||||||
Duration::from_micros(250),
|
Duration::from_micros(250),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tick_internal_client();
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
wayland.update();
|
wayland.update();
|
||||||
drawable::draw(token);
|
drawable::draw(token);
|
||||||
@@ -262,8 +319,6 @@ fn stereokit_loop(
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("Cleanly shut down StereoKit");
|
info!("Cleanly shut down StereoKit");
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
drop(wayland);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adaptive_sleep(
|
fn adaptive_sleep(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use super::{Aspect, Node};
|
use super::{Aspect, AspectIdentifier, Node};
|
||||||
use crate::core::{client::Client, registry::Registry};
|
use crate::core::{client::Client, error::Result, registry::Registry};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use std::{
|
use std::{
|
||||||
ops::Add,
|
ops::Add,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
@@ -68,8 +67,31 @@ impl Alias {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for Alias {
|
||||||
|
const ID: u64 = 0;
|
||||||
|
}
|
||||||
impl Aspect for Alias {
|
impl Aspect for Alias {
|
||||||
const NAME: &'static str = "Alias";
|
fn as_any(self: Arc<Self>) -> Arc<dyn std::any::Any + Send + Sync + 'static> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn run_signal(
|
||||||
|
&self,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
_node: Arc<Node>,
|
||||||
|
_signal: u64,
|
||||||
|
_message: super::Message,
|
||||||
|
) -> Result<(), stardust_xr::scenegraph::ScenegraphError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn run_method(
|
||||||
|
&self,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
_node: Arc<Node>,
|
||||||
|
_method: u64,
|
||||||
|
_message: super::Message,
|
||||||
|
_response: crate::core::scenegraph::MethodResponseSender,
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_original(node: Arc<Node>, stop_on_disabled: bool) -> Option<Arc<Node>> {
|
pub fn get_original(node: Arc<Node>, stop_on_disabled: bool) -> Option<Arc<Node>> {
|
||||||
@@ -106,7 +128,7 @@ impl AliasList {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.find(move |node| links_to(node.clone(), original.clone()))
|
.find(move |node| links_to(node.clone(), original.clone()))
|
||||||
}
|
}
|
||||||
pub fn get_from_aspect<A: Aspect>(&self, aspect: &A) -> Option<Arc<Node>> {
|
pub fn get_from_aspect<A: AspectIdentifier>(&self, aspect: &A) -> Option<Arc<Node>> {
|
||||||
self.0.get_valid_contents().into_iter().find(|node| {
|
self.0.get_valid_contents().into_iter().find(|node| {
|
||||||
let Some(node) = get_original(node.clone(), false) else {
|
let Some(node) = get_original(node.clone(), false) else {
|
||||||
return false;
|
return false;
|
||||||
@@ -120,7 +142,7 @@ impl AliasList {
|
|||||||
pub fn get_aliases(&self) -> Vec<Arc<Node>> {
|
pub fn get_aliases(&self) -> Vec<Arc<Node>> {
|
||||||
self.0.get_valid_contents()
|
self.0.get_valid_contents()
|
||||||
}
|
}
|
||||||
pub fn remove_aspect<A: Aspect>(&self, aspect: &A) {
|
pub fn remove_aspect<A: AspectIdentifier>(&self, aspect: &A) {
|
||||||
self.0.retain(|node| {
|
self.0.retain(|node| {
|
||||||
let Some(original) = get_original(node.clone(), false) else {
|
let Some(original) = get_original(node.clone(), false) else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
use super::{Aspect, Node};
|
use super::{Aspect, AspectIdentifier, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::destroy_queue;
|
use crate::core::destroy_queue;
|
||||||
|
use crate::core::error::Result;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::resource::get_resource_file;
|
use crate::core::resource::get_resource_file;
|
||||||
use crate::create_interface;
|
use crate::nodes::spatial::{SPATIAL_ASPECT_ALIAS_INFO, Spatial, Transform};
|
||||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
use color_eyre::eyre::eyre;
|
||||||
use crate::nodes::spatial::{Spatial, Transform};
|
use glam::{Vec4Swizzles, vec3};
|
||||||
use color_eyre::eyre::{eyre, Result};
|
|
||||||
use glam::{vec3, Vec4Swizzles};
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use stardust_xr::values::ResourceID;
|
use stardust_xr::values::ResourceID;
|
||||||
|
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, OnceLock};
|
||||||
use std::{ffi::OsStr, path::PathBuf};
|
use std::{ffi::OsStr, path::PathBuf};
|
||||||
use stereokit_rust::sound::{Sound as SkSound, SoundInst};
|
use stereokit_rust::sound::{Sound as SkSound, SoundInst};
|
||||||
|
|
||||||
@@ -25,7 +23,7 @@ pub struct Sound {
|
|||||||
|
|
||||||
volume: f32,
|
volume: f32,
|
||||||
pending_audio_path: PathBuf,
|
pending_audio_path: PathBuf,
|
||||||
sk_sound: OnceCell<SkSound>,
|
sk_sound: OnceLock<SkSound>,
|
||||||
instance: Mutex<Option<SoundInst>>,
|
instance: Mutex<Option<SoundInst>>,
|
||||||
stop: Mutex<Option<()>>,
|
stop: Mutex<Option<()>>,
|
||||||
play: Mutex<Option<()>>,
|
play: Mutex<Option<()>>,
|
||||||
@@ -42,14 +40,13 @@ impl Sound {
|
|||||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
pending_audio_path,
|
pending_audio_path,
|
||||||
sk_sound: OnceCell::new(),
|
sk_sound: OnceLock::new(),
|
||||||
instance: Mutex::new(None),
|
instance: Mutex::new(None),
|
||||||
stop: Mutex::new(None),
|
stop: Mutex::new(None),
|
||||||
play: Mutex::new(None),
|
play: Mutex::new(None),
|
||||||
};
|
};
|
||||||
let sound_arc = SOUND_REGISTRY.add(sound);
|
let sound_arc = SOUND_REGISTRY.add(sound);
|
||||||
node.add_aspect_raw(sound_arc.clone());
|
node.add_aspect_raw(sound_arc.clone());
|
||||||
<Sound as SoundAspect>::add_node_members(node);
|
|
||||||
Ok(sound_arc)
|
Ok(sound_arc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +68,11 @@ impl Sound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for Sound {
|
||||||
|
impl_aspect_for_sound_aspect_id! {}
|
||||||
|
}
|
||||||
impl Aspect for Sound {
|
impl Aspect for Sound {
|
||||||
const NAME: &'static str = "Sound";
|
impl_aspect_for_sound_aspect! {}
|
||||||
}
|
}
|
||||||
impl SoundAspect for Sound {
|
impl SoundAspect for Sound {
|
||||||
fn play(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
fn play(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
@@ -88,6 +88,9 @@ impl SoundAspect for Sound {
|
|||||||
}
|
}
|
||||||
impl Drop for Sound {
|
impl Drop for Sound {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
if let Some(instance) = self.instance.lock().take() {
|
||||||
|
instance.stop();
|
||||||
|
}
|
||||||
if let Some(sk_sound) = self.sk_sound.take() {
|
if let Some(sk_sound) = self.sk_sound.take() {
|
||||||
destroy_queue::add(sk_sound);
|
destroy_queue::add(sk_sound);
|
||||||
}
|
}
|
||||||
@@ -101,9 +104,7 @@ pub fn update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_interface!(AudioInterface);
|
impl InterfaceAspect for Interface {
|
||||||
struct AudioInterface;
|
|
||||||
impl InterfaceAspect for AudioInterface {
|
|
||||||
#[doc = "Create a sound node. WAV and MP3 are supported."]
|
#[doc = "Create a sound node. WAV and MP3 are supported."]
|
||||||
fn create_sound(
|
fn create_sound(
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
|
|||||||
@@ -1,276 +0,0 @@
|
|||||||
use super::alias::AliasList;
|
|
||||||
use super::fields::Field;
|
|
||||||
use super::spatial::{parse_transform, Spatial};
|
|
||||||
use super::{Alias, Aspect, Node};
|
|
||||||
use crate::core::client::Client;
|
|
||||||
use crate::core::registry::Registry;
|
|
||||||
use crate::create_interface;
|
|
||||||
use crate::nodes::fields::FIELD_ALIAS_INFO;
|
|
||||||
use crate::nodes::spatial::Transform;
|
|
||||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use slotmap::{DefaultKey, Key, KeyData, SlotMap};
|
|
||||||
use stardust_xr::schemas::flex::flexbuffers;
|
|
||||||
use stardust_xr::values::Datamap;
|
|
||||||
use std::sync::{Arc, Weak};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref KEYMAPS: Mutex<SlotMap<DefaultKey, String>> = Mutex::new(SlotMap::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: probably just use d-bus for this stuff (custom protocol for exporting spatials as refs) because the mask stuff is just too confusing
|
|
||||||
|
|
||||||
static PULSE_SENDER_REGISTRY: Registry<PulseSender> = Registry::new();
|
|
||||||
pub static PULSE_RECEIVER_REGISTRY: Registry<PulseReceiver> = Registry::new();
|
|
||||||
|
|
||||||
pub fn get_mask(datamap: &Datamap) -> Result<flexbuffers::MapReader<&[u8]>> {
|
|
||||||
flexbuffers::Reader::get_root(datamap.raw().as_slice())
|
|
||||||
.map_err(|_| eyre!("Mask is not a valid flexbuffer"))?
|
|
||||||
.get_map()
|
|
||||||
.map_err(|_| eyre!("Mask is not a valid map"))
|
|
||||||
}
|
|
||||||
pub fn mask_matches(mask_map_lesser: &Datamap, mask_map_greater: &Datamap) -> bool {
|
|
||||||
(|| -> Result<_> {
|
|
||||||
for key in get_mask(mask_map_lesser)?.iter_keys() {
|
|
||||||
let lesser_key = get_mask(mask_map_lesser)?.index(key)?;
|
|
||||||
let greater_key = get_mask(mask_map_greater)?.index(key)?;
|
|
||||||
// otherwise zero-length vectors don't count the same as a single type vector
|
|
||||||
if lesser_key.flexbuffer_type().is_heterogenous_vector()
|
|
||||||
&& lesser_key.as_vector().is_empty()
|
|
||||||
&& greater_key.flexbuffer_type().is_vector()
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !lesser_key.flexbuffer_type().is_null()
|
|
||||||
&& lesser_key.flexbuffer_type() != greater_key.flexbuffer_type()
|
|
||||||
{
|
|
||||||
return Err(flexbuffers::ReaderError::InvalidPackedType {}.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})()
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
stardust_xr_server_codegen::codegen_data_protocol!();
|
|
||||||
|
|
||||||
pub struct PulseSender {
|
|
||||||
node: Weak<Node>,
|
|
||||||
pub mask: Datamap,
|
|
||||||
aliases: AliasList,
|
|
||||||
field_aliases: AliasList,
|
|
||||||
}
|
|
||||||
impl PulseSender {
|
|
||||||
pub fn add_to(node: &Arc<Node>, mask: Datamap) -> Result<Arc<PulseSender>> {
|
|
||||||
let sender = PulseSender {
|
|
||||||
node: Arc::downgrade(node),
|
|
||||||
mask,
|
|
||||||
aliases: AliasList::default(),
|
|
||||||
field_aliases: AliasList::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// <PulseSender as PulseSenderAspect>::add_node_members(node);
|
|
||||||
let sender = PULSE_SENDER_REGISTRY.add(sender);
|
|
||||||
node.add_aspect_raw(sender.clone());
|
|
||||||
for receiver in PULSE_RECEIVER_REGISTRY.get_valid_contents() {
|
|
||||||
sender.handle_new_receiver(&receiver);
|
|
||||||
}
|
|
||||||
Ok(sender.clone())
|
|
||||||
}
|
|
||||||
fn handle_new_receiver(&self, receiver: &PulseReceiver) {
|
|
||||||
if !mask_matches(&self.mask, &receiver.mask) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Some(tx_node) = self.node.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(tx_client) = tx_node.get_client() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(rx_node) = receiver.node.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// Receiver itself
|
|
||||||
let Ok(rx_alias) = Alias::create(
|
|
||||||
&rx_node,
|
|
||||||
&tx_client,
|
|
||||||
PULSE_RECEIVER_ASPECT_ALIAS_INFO.clone(),
|
|
||||||
Some(&self.aliases),
|
|
||||||
) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Receiver's field
|
|
||||||
let Ok(rx_field_alias) = Alias::create(
|
|
||||||
&rx_node
|
|
||||||
.get_aspect::<PulseReceiver>()
|
|
||||||
.unwrap()
|
|
||||||
.field
|
|
||||||
.spatial
|
|
||||||
.node()
|
|
||||||
.unwrap(),
|
|
||||||
&tx_client,
|
|
||||||
FIELD_ALIAS_INFO.clone(),
|
|
||||||
Some(&self.aliases),
|
|
||||||
) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = pulse_sender_client::new_receiver(&tx_node, &rx_alias, &rx_field_alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_drop_receiver(&self, receiver: &PulseReceiver) {
|
|
||||||
let Some(node) = receiver.node.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
self.aliases.remove_aspect(receiver);
|
|
||||||
self.field_aliases.remove_aspect(receiver.field.as_ref());
|
|
||||||
let Some(tx_node) = self.node.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let _ = pulse_sender_client::drop_receiver(&tx_node, node.get_id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Aspect for PulseSender {
|
|
||||||
const NAME: &'static str = "PulseSender";
|
|
||||||
}
|
|
||||||
impl PulseSenderAspect for PulseSender {}
|
|
||||||
impl Drop for PulseSender {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
PULSE_SENDER_REGISTRY.remove(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PulseReceiver {
|
|
||||||
pub node: Weak<Node>,
|
|
||||||
pub field: Arc<Field>,
|
|
||||||
pub mask: Datamap,
|
|
||||||
}
|
|
||||||
impl PulseReceiver {
|
|
||||||
pub fn add_to(
|
|
||||||
node: &Arc<Node>,
|
|
||||||
field: Arc<Field>,
|
|
||||||
mask: Datamap,
|
|
||||||
) -> Result<Arc<PulseReceiver>> {
|
|
||||||
let receiver = PulseReceiver {
|
|
||||||
node: Arc::downgrade(node),
|
|
||||||
field,
|
|
||||||
mask,
|
|
||||||
};
|
|
||||||
let receiver = PULSE_RECEIVER_REGISTRY.add(receiver);
|
|
||||||
|
|
||||||
<PulseReceiver as PulseReceiverAspect>::add_node_members(node);
|
|
||||||
node.add_aspect_raw(receiver.clone());
|
|
||||||
for sender in PULSE_SENDER_REGISTRY.get_valid_contents() {
|
|
||||||
sender.handle_new_receiver(&receiver);
|
|
||||||
}
|
|
||||||
Ok(receiver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Aspect for PulseReceiver {
|
|
||||||
const NAME: &'static str = "PulseReceiver";
|
|
||||||
}
|
|
||||||
impl PulseReceiverAspect for PulseReceiver {
|
|
||||||
fn send_data(
|
|
||||||
node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
sender: Arc<Node>,
|
|
||||||
data: Datamap,
|
|
||||||
) -> Result<()> {
|
|
||||||
let this_receiver = node.get_aspect::<PulseReceiver>().unwrap();
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
mask_matches(&this_receiver.mask, &data),
|
|
||||||
"Message ({data:?}) does not contain the same keys as the receiver's mask ({:?})",
|
|
||||||
this_receiver.mask
|
|
||||||
);
|
|
||||||
pulse_receiver_client::data(&node, &sender, &data)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for PulseReceiver {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
PULSE_RECEIVER_REGISTRY.remove(self);
|
|
||||||
for sender in PULSE_SENDER_REGISTRY.get_valid_contents() {
|
|
||||||
sender.handle_drop_receiver(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
create_interface!(DataInterface);
|
|
||||||
struct DataInterface;
|
|
||||||
impl InterfaceAspect for DataInterface {
|
|
||||||
fn create_pulse_sender(
|
|
||||||
_node: Arc<Node>,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
id: u64,
|
|
||||||
parent: Arc<Node>,
|
|
||||||
transform: Transform,
|
|
||||||
mask: Datamap,
|
|
||||||
) -> Result<()> {
|
|
||||||
get_mask(&mask)?;
|
|
||||||
let node = Node::from_id(&calling_client, id, true);
|
|
||||||
let parent = parent.get_aspect::<Spatial>()?;
|
|
||||||
let transform = transform.to_mat4(true, true, false);
|
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
|
||||||
PulseSender::add_to(&node, mask)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_pulse_receiver(
|
|
||||||
_node: Arc<Node>,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
id: u64,
|
|
||||||
parent: Arc<Node>,
|
|
||||||
transform: Transform,
|
|
||||||
field: Arc<Node>,
|
|
||||||
mask: Datamap,
|
|
||||||
) -> Result<()> {
|
|
||||||
get_mask(&mask)?;
|
|
||||||
let node = Node::from_id(&calling_client, id, true);
|
|
||||||
let parent = parent.get_aspect::<Spatial>()?;
|
|
||||||
let transform = parse_transform(transform, true, true, false);
|
|
||||||
let field = field.get_aspect::<Field>()?;
|
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
|
||||||
PulseReceiver::add_to(&node, field, mask)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn register_keymap(
|
|
||||||
_node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
keymap: String,
|
|
||||||
) -> Result<u64> {
|
|
||||||
let mut keymaps = KEYMAPS.lock();
|
|
||||||
if let Some(found_keymap_id) = keymaps
|
|
||||||
.iter()
|
|
||||||
.filter(|(_k, v)| *v == &keymap)
|
|
||||||
.map(|(k, _v)| k)
|
|
||||||
.last()
|
|
||||||
{
|
|
||||||
return Ok(found_keymap_id.data().as_ffi());
|
|
||||||
}
|
|
||||||
|
|
||||||
let key = keymaps.insert(keymap);
|
|
||||||
Ok(key.data().as_ffi())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_keymap(
|
|
||||||
_node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
keymap_id: u64,
|
|
||||||
) -> Result<String> {
|
|
||||||
let keymaps = KEYMAPS.lock();
|
|
||||||
let Some(keymap) = keymaps.get(KeyData::from_ffi(keymap_id).into()) else {
|
|
||||||
bail!("Could not find keymap. Try registering it")
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(keymap.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
use super::{Line, LinesAspect};
|
use super::{Line, LinesAspect};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, registry::Registry},
|
core::{client::Client, error::Result, registry::Registry},
|
||||||
nodes::{spatial::Spatial, Aspect, Node},
|
nodes::{Node, spatial::Spatial},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use glam::{FloatExt, Vec3};
|
||||||
use glam::Vec3;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use prisma::Lerp;
|
|
||||||
use std::{collections::VecDeque, sync::Arc};
|
use std::{collections::VecDeque, sync::Arc};
|
||||||
use stereokit_rust::{
|
use stereokit_rust::{
|
||||||
maths::Bounds, sk::MainThreadToken, system::LinePoint as SkLinePoint, util::Color128,
|
maths::Bounds, sk::MainThreadToken, system::LinePoint as SkLinePoint, util::Color128,
|
||||||
@@ -40,7 +38,6 @@ impl Lines {
|
|||||||
space: node.get_aspect::<Spatial>()?.clone(),
|
space: node.get_aspect::<Spatial>()?.clone(),
|
||||||
data: Mutex::new(lines),
|
data: Mutex::new(lines),
|
||||||
});
|
});
|
||||||
<Lines as LinesAspect>::add_node_members(node);
|
|
||||||
node.add_aspect_raw(lines.clone());
|
node.add_aspect_raw(lines.clone());
|
||||||
|
|
||||||
Ok(lines)
|
Ok(lines)
|
||||||
@@ -64,10 +61,10 @@ impl Lines {
|
|||||||
let last = line.points.last().unwrap();
|
let last = line.points.last().unwrap();
|
||||||
|
|
||||||
let color = Color128 {
|
let color = Color128 {
|
||||||
r: first.color.c.r.lerp(&last.color.c.r, 0.5),
|
r: first.color.c.r.lerp(last.color.c.r, 0.5),
|
||||||
g: first.color.c.g.lerp(&last.color.c.g, 0.5),
|
g: first.color.c.g.lerp(last.color.c.g, 0.5),
|
||||||
b: first.color.c.b.lerp(&last.color.c.b, 0.5),
|
b: first.color.c.b.lerp(last.color.c.b, 0.5),
|
||||||
a: first.color.a.lerp(&last.color.a, 0.5),
|
a: first.color.a.lerp(last.color.a, 0.5),
|
||||||
};
|
};
|
||||||
let connect_point = SkLinePoint {
|
let connect_point = SkLinePoint {
|
||||||
pt: transform_mat
|
pt: transform_mat
|
||||||
@@ -83,9 +80,6 @@ impl Lines {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for Lines {
|
|
||||||
const NAME: &'static str = "Lines";
|
|
||||||
}
|
|
||||||
impl LinesAspect for Lines {
|
impl LinesAspect for Lines {
|
||||||
fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> {
|
fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> {
|
||||||
let lines_aspect = node.get_aspect::<Lines>()?;
|
let lines_aspect = node.get_aspect::<Lines>()?;
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
pub mod lines;
|
pub mod lines;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
pub mod shader_manipulation;
|
|
||||||
pub mod shaders;
|
pub mod shaders;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
use self::{lines::Lines, model::Model, text::Text};
|
use self::{lines::Lines, model::Model, text::Text};
|
||||||
use super::{
|
use super::{
|
||||||
|
Aspect, AspectIdentifier, Node,
|
||||||
spatial::{Spatial, Transform},
|
spatial::{Spatial, Transform},
|
||||||
Node,
|
|
||||||
};
|
};
|
||||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
use crate::{DEFAULT_SKYLIGHT, nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, resource::get_resource_file},
|
DEFAULT_SKYTEX,
|
||||||
create_interface,
|
core::{client::Client, error::Result, resource::get_resource_file},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{self, Result};
|
use color_eyre::eyre::eyre;
|
||||||
|
use model::ModelPart;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use stardust_xr::values::ResourceID;
|
use stardust_xr::values::ResourceID;
|
||||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
||||||
@@ -26,30 +25,72 @@ pub fn draw(token: &MainThreadToken) {
|
|||||||
lines::draw_all(token);
|
lines::draw_all(token);
|
||||||
model::draw_all(token);
|
model::draw_all(token);
|
||||||
text::draw_all(token);
|
text::draw_all(token);
|
||||||
|
match QUEUED_SKYTEX.lock().take() {
|
||||||
if let Some(skytex) = QUEUED_SKYTEX.lock().take() {
|
Some(Some(skytex)) => {
|
||||||
if let Ok(skytex) = SHCubemap::from_cubemap_equirectangular(skytex, true, 100) {
|
if let Ok(skytex) = SHCubemap::from_cubemap(skytex, true, 100) {
|
||||||
Renderer::skytex(skytex.tex);
|
Renderer::skytex(skytex.tex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some(None) => {
|
||||||
|
Renderer::skytex(DEFAULT_SKYTEX.get().unwrap());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
if let Some(skylight) = QUEUED_SKYLIGHT.lock().take() {
|
match QUEUED_SKYLIGHT.lock().take() {
|
||||||
if let Ok(skylight) = SHCubemap::from_cubemap_equirectangular(skylight, true, 100) {
|
Some(Some(skylight)) => {
|
||||||
Renderer::skylight(skylight.sh);
|
if let Ok(skylight) = SHCubemap::from_cubemap(skylight, true, 100) {
|
||||||
|
Renderer::skylight(skylight.sh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some(None) => {
|
||||||
|
Renderer::skylight(*DEFAULT_SKYLIGHT.get().unwrap());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QUEUED_SKYLIGHT: Mutex<Option<PathBuf>> = Mutex::new(None);
|
static QUEUED_SKYLIGHT: Mutex<Option<Option<PathBuf>>> = Mutex::new(None);
|
||||||
static QUEUED_SKYTEX: Mutex<Option<PathBuf>> = Mutex::new(None);
|
static QUEUED_SKYTEX: Mutex<Option<Option<PathBuf>>> = Mutex::new(None);
|
||||||
|
|
||||||
stardust_xr_server_codegen::codegen_drawable_protocol!();
|
stardust_xr_server_codegen::codegen_drawable_protocol!();
|
||||||
create_interface!(DrawableInterface);
|
|
||||||
|
|
||||||
pub struct DrawableInterface;
|
impl AspectIdentifier for Lines {
|
||||||
impl InterfaceAspect for DrawableInterface {
|
impl_aspect_for_lines_aspect_id! {}
|
||||||
fn set_sky_tex(_node: Arc<Node>, calling_client: Arc<Client>, tex: ResourceID) -> Result<()> {
|
}
|
||||||
let resource_path = get_resource_file(&tex, &calling_client, &[OsStr::new("hdr")])
|
impl Aspect for Lines {
|
||||||
.ok_or(eyre::eyre!("Could not find resource"))?;
|
impl_aspect_for_lines_aspect! {}
|
||||||
|
}
|
||||||
|
impl AspectIdentifier for Model {
|
||||||
|
impl_aspect_for_model_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for Model {
|
||||||
|
impl_aspect_for_model_aspect! {}
|
||||||
|
}
|
||||||
|
impl AspectIdentifier for ModelPart {
|
||||||
|
impl_aspect_for_model_part_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for ModelPart {
|
||||||
|
impl_aspect_for_model_part_aspect! {}
|
||||||
|
}
|
||||||
|
impl AspectIdentifier for Text {
|
||||||
|
impl_aspect_for_text_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for Text {
|
||||||
|
impl_aspect_for_text_aspect! {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterfaceAspect for Interface {
|
||||||
|
fn set_sky_tex(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
tex: Option<ResourceID>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let resource_path = tex
|
||||||
|
.map(|tex| {
|
||||||
|
get_resource_file(&tex, &calling_client, &[OsStr::new("hdr")])
|
||||||
|
.ok_or(eyre!("Could not find resource"))
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
QUEUED_SKYTEX.lock().replace(resource_path);
|
QUEUED_SKYTEX.lock().replace(resource_path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -57,10 +98,14 @@ impl InterfaceAspect for DrawableInterface {
|
|||||||
fn set_sky_light(
|
fn set_sky_light(
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
light: ResourceID,
|
light: Option<ResourceID>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let resource_path = get_resource_file(&light, &calling_client, &[OsStr::new("hdr")])
|
let resource_path = light
|
||||||
.ok_or(eyre::eyre!("Could not find resource"))?;
|
.map(|light| {
|
||||||
|
get_resource_file(&light, &calling_client, &[OsStr::new("hdr")])
|
||||||
|
.ok_or(eyre!("Could not find resource"))
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
QUEUED_SKYLIGHT.lock().replace(resource_path);
|
QUEUED_SKYLIGHT.lock().replace(resource_path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,38 @@
|
|||||||
use super::{MaterialParameter, ModelAspect, ModelPartAspect, MODEL_PART_ASPECT_ALIAS_INFO};
|
use super::{MODEL_PART_ASPECT_ALIAS_INFO, MaterialParameter, ModelAspect, ModelPartAspect};
|
||||||
|
use crate::bail;
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
|
use crate::core::error::Result;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::resource::get_resource_file;
|
use crate::core::resource::get_resource_file;
|
||||||
|
use crate::nodes::Node;
|
||||||
use crate::nodes::alias::{Alias, AliasList};
|
use crate::nodes::alias::{Alias, AliasList};
|
||||||
use crate::nodes::spatial::Spatial;
|
use crate::nodes::spatial::Spatial;
|
||||||
use crate::nodes::{Aspect, Node};
|
use color_eyre::eyre::eyre;
|
||||||
use color_eyre::eyre::{bail, eyre, Result};
|
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stardust_xr::values::ResourceID;
|
use stardust_xr::values::ResourceID;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, LazyLock, OnceLock, Weak};
|
||||||
use stereokit_rust::material::Transparency;
|
use stereokit_rust::material::Transparency;
|
||||||
use stereokit_rust::maths::Bounds;
|
use stereokit_rust::maths::Bounds;
|
||||||
use stereokit_rust::sk::MainThreadToken;
|
use stereokit_rust::sk::MainThreadToken;
|
||||||
use stereokit_rust::{material::Material, model::Model as SKModel, tex::Tex, util::Color128};
|
use stereokit_rust::{material::Material, model::Model as SKModel, tex::Tex, util::Color128};
|
||||||
|
|
||||||
pub struct MaterialWrapper(pub Material);
|
pub struct MaterialWrapper(pub Material);
|
||||||
|
impl Drop for MaterialWrapper {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
MATERIAL_REGISTRY.remove(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Hash for MaterialWrapper {
|
impl Hash for MaterialWrapper {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.get_shader().0.as_ptr().hash(state);
|
self.0.get_shader().0.as_ptr().hash(state);
|
||||||
for param in self.0.get_all_param_info() {
|
for param in self.0.get_all_param_info() {
|
||||||
param.to_string().hash(state)
|
param.name.hash(state);
|
||||||
|
param.to_string().hash(state);
|
||||||
}
|
}
|
||||||
self.0.get_chain().map(MaterialWrapper).hash(state)
|
self.0.get_chain().map(MaterialWrapper).hash(state)
|
||||||
}
|
}
|
||||||
@@ -57,10 +65,9 @@ unsafe impl Send for MaterialWrapper {}
|
|||||||
unsafe impl Sync for MaterialWrapper {}
|
unsafe impl Sync for MaterialWrapper {}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct MaterialRegistry(Mutex<FxHashMap<u64, String>>);
|
struct MaterialRegistry(Mutex<FxHashMap<u64, Weak<MaterialWrapper>>>);
|
||||||
impl MaterialRegistry {
|
impl MaterialRegistry {
|
||||||
fn add_or_get(&self, material: Arc<MaterialWrapper>) -> Arc<MaterialWrapper> {
|
fn add_or_get(&self, material: Arc<MaterialWrapper>) -> Arc<MaterialWrapper> {
|
||||||
let mut lock = self.0.lock();
|
|
||||||
let hash = {
|
let hash = {
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
@@ -68,20 +75,31 @@ impl MaterialRegistry {
|
|||||||
hasher.finish()
|
hasher.finish()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(id) = lock.get(&hash) {
|
let mut lock = self.0.lock();
|
||||||
if let Ok(existing) = Material::find(id) {
|
if let Some(mat) = lock.get(&hash) {
|
||||||
return Arc::new(MaterialWrapper(existing));
|
if let Some(mat) = mat.upgrade() {
|
||||||
|
return mat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.insert(hash, material.0.get_id().to_string());
|
lock.insert(hash, Arc::downgrade(&material));
|
||||||
material
|
material
|
||||||
}
|
}
|
||||||
|
fn remove(&self, material: &MaterialWrapper) {
|
||||||
|
let hash = {
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
material.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
};
|
||||||
|
let mut lock = self.0.lock();
|
||||||
|
lock.remove(&hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static MATERIAL_REGISTRY: Lazy<MaterialRegistry> = Lazy::new(MaterialRegistry::default);
|
static MATERIAL_REGISTRY: LazyLock<MaterialRegistry> = LazyLock::new(MaterialRegistry::default);
|
||||||
static MODEL_REGISTRY: Registry<Model> = Registry::new();
|
static MODEL_REGISTRY: Registry<Model> = Registry::new();
|
||||||
static HOLDOUT_MATERIAL: OnceCell<Arc<MaterialWrapper>> = OnceCell::new();
|
static HOLDOUT_MATERIAL: OnceLock<Arc<MaterialWrapper>> = OnceLock::new();
|
||||||
|
|
||||||
impl MaterialParameter {
|
impl MaterialParameter {
|
||||||
fn apply_to_material(&self, client: &Client, material: &Material, parameter_name: &str) {
|
fn apply_to_material(&self, client: &Client, material: &Material, parameter_name: &str) {
|
||||||
@@ -130,6 +148,7 @@ pub struct ModelPart {
|
|||||||
path: String,
|
path: String,
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
model: Weak<Model>,
|
model: Weak<Model>,
|
||||||
|
material: Mutex<Option<Arc<MaterialWrapper>>>,
|
||||||
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
|
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
|
||||||
pending_material_replacement: Mutex<Option<Arc<MaterialWrapper>>>,
|
pending_material_replacement: Mutex<Option<Arc<MaterialWrapper>>>,
|
||||||
aliases: AliasList,
|
aliases: AliasList,
|
||||||
@@ -203,8 +222,8 @@ impl ModelPart {
|
|||||||
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
||||||
pending_material_replacement: Mutex::new(None),
|
pending_material_replacement: Mutex::new(None),
|
||||||
aliases: AliasList::default(),
|
aliases: AliasList::default(),
|
||||||
|
material: Mutex::new(part.get_material().map(MaterialWrapper).map(Arc::new)),
|
||||||
});
|
});
|
||||||
<ModelPart as ModelPartAspect>::add_node_members(&node);
|
|
||||||
node.add_aspect_raw(model_part.clone());
|
node.add_aspect_raw(model_part.clone());
|
||||||
parts.push(model_part.clone());
|
parts.push(model_part.clone());
|
||||||
Some(model_part)
|
Some(model_part)
|
||||||
@@ -230,7 +249,10 @@ impl ModelPart {
|
|||||||
};
|
};
|
||||||
let shared_material =
|
let shared_material =
|
||||||
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(replacement.copy())));
|
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(replacement.copy())));
|
||||||
|
|
||||||
|
let mut lock = self.material.lock();
|
||||||
part.material(&shared_material.0);
|
part.material(&shared_material.0);
|
||||||
|
lock.replace(shared_material);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) {
|
fn update(&self) {
|
||||||
@@ -257,7 +279,9 @@ impl ModelPart {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
|
if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
|
||||||
|
let mut lock = self.material.lock();
|
||||||
part.material(&material_replacement.0);
|
part.material(&material_replacement.0);
|
||||||
|
lock.replace(material_replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
'mat_params: {
|
'mat_params: {
|
||||||
@@ -273,14 +297,13 @@ impl ModelPart {
|
|||||||
|
|
||||||
let shared_material =
|
let shared_material =
|
||||||
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(new_material)));
|
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(new_material)));
|
||||||
|
let mut lock = self.material.lock();
|
||||||
part.material(&shared_material.0);
|
part.material(&shared_material.0);
|
||||||
|
lock.replace(shared_material);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for ModelPart {
|
|
||||||
const NAME: &'static str = "ModelPart";
|
|
||||||
}
|
|
||||||
impl ModelPartAspect for ModelPart {
|
impl ModelPartAspect for ModelPart {
|
||||||
#[doc = "Set this model part's material to one that cuts a hole in the world. Often used for overlays/passthrough where you want to show the background through an object."]
|
#[doc = "Set this model part's material to one that cuts a hole in the world. Often used for overlays/passthrough where you want to show the background through an object."]
|
||||||
fn apply_holdout_material(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
fn apply_holdout_material(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
@@ -309,7 +332,7 @@ impl ModelPartAspect for ModelPart {
|
|||||||
pub struct Model {
|
pub struct Model {
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
_resource_id: ResourceID,
|
_resource_id: ResourceID,
|
||||||
sk_model: OnceCell<SKModel>,
|
sk_model: OnceLock<SKModel>,
|
||||||
parts: Mutex<Vec<Arc<ModelPart>>>,
|
parts: Mutex<Vec<Arc<ModelPart>>>,
|
||||||
}
|
}
|
||||||
impl Model {
|
impl Model {
|
||||||
@@ -324,10 +347,9 @@ impl Model {
|
|||||||
let model = Arc::new(Model {
|
let model = Arc::new(Model {
|
||||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
_resource_id: resource_id,
|
_resource_id: resource_id,
|
||||||
sk_model: OnceCell::new(),
|
sk_model: OnceLock::new(),
|
||||||
parts: Mutex::new(Vec::default()),
|
parts: Mutex::new(Vec::default()),
|
||||||
});
|
});
|
||||||
<Model as ModelAspect>::add_node_members(node);
|
|
||||||
MODEL_REGISTRY.add_raw(&model);
|
MODEL_REGISTRY.add_raw(&model);
|
||||||
|
|
||||||
// technically doing this in anything but the main thread isn't a good idea but dangit we need those model nodes ASAP
|
// technically doing this in anything but the main thread isn't a good idea but dangit we need those model nodes ASAP
|
||||||
@@ -361,9 +383,6 @@ impl Model {
|
|||||||
// TODO: proper hread safety in stereokit_rust (probably just bind stereokit directly)
|
// TODO: proper hread safety in stereokit_rust (probably just bind stereokit directly)
|
||||||
unsafe impl Send for Model {}
|
unsafe impl Send for Model {}
|
||||||
unsafe impl Sync for Model {}
|
unsafe impl Sync for Model {}
|
||||||
impl Aspect for Model {
|
|
||||||
const NAME: &'static str = "Model";
|
|
||||||
}
|
|
||||||
impl ModelAspect for Model {
|
impl ModelAspect for Model {
|
||||||
#[doc = "Bind a model part to the node with the ID input."]
|
#[doc = "Bind a model part to the node with the ID input."]
|
||||||
fn bind_model_part(
|
fn bind_model_part(
|
||||||
@@ -371,12 +390,12 @@ impl ModelAspect for Model {
|
|||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
id: u64,
|
id: u64,
|
||||||
part_path: String,
|
part_path: String,
|
||||||
) -> color_eyre::eyre::Result<()> {
|
) -> Result<()> {
|
||||||
let model = node.get_aspect::<Model>()?;
|
let model = node.get_aspect::<Model>()?;
|
||||||
let parts = model.parts.lock();
|
let parts = model.parts.lock();
|
||||||
let Some(part) = parts.iter().find(|p| p.path == part_path) else {
|
let Some(part) = parts.iter().find(|p| p.path == part_path) else {
|
||||||
let paths = parts.iter().map(|p| &p.path).collect::<Vec<_>>();
|
let paths = parts.iter().map(|p| &p.path).collect::<Vec<_>>();
|
||||||
bail!("Couldn't find model part at path {part_path}, all available paths: {paths:?}",)
|
bail!("Couldn't find model part at path {part_path}, all available paths: {paths:?}",);
|
||||||
};
|
};
|
||||||
Alias::create_with_id(
|
Alias::create_with_id(
|
||||||
&part.space.node().unwrap(),
|
&part.space.node().unwrap(),
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use smithay::backend::renderer::gles::{
|
|
||||||
ffi::{self, Gles2, FRAGMENT_SHADER, VERTEX_SHADER},
|
|
||||||
GlesError,
|
|
||||||
};
|
|
||||||
use stereokit_rust::shader::{Shader, _ShaderT};
|
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
struct FfiAssetHeader {
|
|
||||||
asset_type: i32,
|
|
||||||
asset_state: i32,
|
|
||||||
id: u64,
|
|
||||||
index: u64,
|
|
||||||
refs: i32,
|
|
||||||
debug: *mut u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FfiSkgShader {
|
|
||||||
meta: *mut u8,
|
|
||||||
vertex: u32,
|
|
||||||
pixel: u32,
|
|
||||||
program: u32,
|
|
||||||
compute: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FfiShader {
|
|
||||||
header: FfiAssetHeader,
|
|
||||||
shader: FfiSkgShader,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn compile_shader(
|
|
||||||
gl: &ffi::Gles2,
|
|
||||||
variant: ffi::types::GLuint,
|
|
||||||
src: &str,
|
|
||||||
) -> Result<ffi::types::GLuint, GlesError> {
|
|
||||||
let shader = gl.CreateShader(variant);
|
|
||||||
if shader == 0 {
|
|
||||||
return Err(GlesError::CreateShaderObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.ShaderSource(
|
|
||||||
shader,
|
|
||||||
1,
|
|
||||||
&src.as_ptr() as *const *const u8 as *const *const ffi::types::GLchar,
|
|
||||||
&(src.len() as i32) as *const _,
|
|
||||||
);
|
|
||||||
gl.CompileShader(shader);
|
|
||||||
|
|
||||||
let mut status = ffi::FALSE as i32;
|
|
||||||
gl.GetShaderiv(shader, ffi::COMPILE_STATUS, &mut status as *mut _);
|
|
||||||
if status == ffi::FALSE as i32 {
|
|
||||||
let mut max_len = 0;
|
|
||||||
gl.GetShaderiv(shader, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _);
|
|
||||||
|
|
||||||
let mut error = Vec::with_capacity(max_len as usize);
|
|
||||||
let mut len = 0;
|
|
||||||
gl.GetShaderInfoLog(
|
|
||||||
shader,
|
|
||||||
max_len as _,
|
|
||||||
&mut len as *mut _,
|
|
||||||
error.as_mut_ptr() as *mut _,
|
|
||||||
);
|
|
||||||
error.set_len(len as usize);
|
|
||||||
|
|
||||||
error!(
|
|
||||||
"[GL] {}",
|
|
||||||
std::str::from_utf8(&error).unwrap_or("<Error Message no utf8>")
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.DeleteShader(shader);
|
|
||||||
return Err(GlesError::ShaderCompileError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(shader)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn link_program(
|
|
||||||
gl: &ffi::Gles2,
|
|
||||||
vert: ffi::types::GLuint,
|
|
||||||
frag: ffi::types::GLuint,
|
|
||||||
) -> Result<ffi::types::GLuint, GlesError> {
|
|
||||||
let program = gl.CreateProgram();
|
|
||||||
gl.AttachShader(program, vert);
|
|
||||||
gl.AttachShader(program, frag);
|
|
||||||
gl.LinkProgram(program);
|
|
||||||
// gl.DetachShader(program, vert);
|
|
||||||
// gl.DetachShader(program, frag);
|
|
||||||
// gl.DeleteShader(vert);
|
|
||||||
// gl.DeleteShader(frag);
|
|
||||||
|
|
||||||
let mut status = ffi::FALSE as i32;
|
|
||||||
gl.GetProgramiv(program, ffi::LINK_STATUS, &mut status as *mut _);
|
|
||||||
if status == ffi::FALSE as i32 {
|
|
||||||
let mut max_len = 0;
|
|
||||||
gl.GetProgramiv(program, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _);
|
|
||||||
|
|
||||||
let mut error = Vec::with_capacity(max_len as usize);
|
|
||||||
let mut len = 0;
|
|
||||||
gl.GetProgramInfoLog(
|
|
||||||
program,
|
|
||||||
max_len as _,
|
|
||||||
&mut len as *mut _,
|
|
||||||
error.as_mut_ptr() as *mut _,
|
|
||||||
);
|
|
||||||
error.set_len(len as usize);
|
|
||||||
|
|
||||||
error!(
|
|
||||||
"[GL] {}",
|
|
||||||
std::str::from_utf8(&error).unwrap_or("<Error Message no utf8>")
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.DeleteProgram(program);
|
|
||||||
return Err(GlesError::ProgramLinkError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(program)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn shader_inject(
|
|
||||||
c: &Gles2,
|
|
||||||
sk_shader: &mut Shader,
|
|
||||||
vert_str: &str,
|
|
||||||
frag_str: &str,
|
|
||||||
) -> Result<(), GlesError> {
|
|
||||||
let gl_vert = compile_shader(c, VERTEX_SHADER, vert_str)?;
|
|
||||||
let gl_frag = compile_shader(c, FRAGMENT_SHADER, frag_str)?;
|
|
||||||
let gl_prog = link_program(c, gl_vert, gl_frag)?;
|
|
||||||
|
|
||||||
let shader = sk_shader.0.as_mut() as *mut _ShaderT as *mut FfiShader;
|
|
||||||
if let Some(shader) = shader.as_mut() {
|
|
||||||
shader.shader.vertex = gl_vert;
|
|
||||||
shader.shader.pixel = gl_frag;
|
|
||||||
shader.shader.program = gl_prog;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,23 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, destroy_queue, registry::Registry, resource::get_resource_file},
|
core::{
|
||||||
nodes::{spatial::Spatial, Aspect, Node},
|
client::Client, destroy_queue, error::Result, registry::Registry,
|
||||||
|
resource::get_resource_file,
|
||||||
|
},
|
||||||
|
nodes::{Node, spatial::Spatial},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::eyre;
|
||||||
use glam::{vec3, Mat4, Vec2};
|
use glam::{Mat4, Vec2, vec3};
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, OnceLock},
|
||||||
|
};
|
||||||
use stereokit_rust::{
|
use stereokit_rust::{
|
||||||
font::Font,
|
font::Font,
|
||||||
sk::MainThreadToken,
|
sk::MainThreadToken,
|
||||||
system::{TextAlign, TextFit, TextStyle as SkTextStyle},
|
system::{TextAlign, TextFit, TextStyle as SkTextStyle},
|
||||||
util::{Color128, Color32},
|
util::{Color32, Color128},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{TextAspect, TextStyle};
|
use super::{TextAspect, TextStyle};
|
||||||
@@ -35,7 +41,7 @@ fn convert_align(x_align: super::XAlign, y_align: super::YAlign) -> TextAlign {
|
|||||||
pub struct Text {
|
pub struct Text {
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
font_path: Option<PathBuf>,
|
font_path: Option<PathBuf>,
|
||||||
style: OnceCell<SkTextStyle>,
|
style: OnceLock<SkTextStyle>,
|
||||||
|
|
||||||
text: Mutex<String>,
|
text: Mutex<String>,
|
||||||
data: Mutex<TextStyle>,
|
data: Mutex<TextStyle>,
|
||||||
@@ -48,93 +54,85 @@ impl Text {
|
|||||||
font_path: style.font.as_ref().and_then(|res| {
|
font_path: style.font.as_ref().and_then(|res| {
|
||||||
get_resource_file(res, &client, &[OsStr::new("ttf"), OsStr::new("otf")])
|
get_resource_file(res, &client, &[OsStr::new("ttf"), OsStr::new("otf")])
|
||||||
}),
|
}),
|
||||||
style: OnceCell::new(),
|
style: OnceLock::new(),
|
||||||
|
|
||||||
text: Mutex::new(text),
|
text: Mutex::new(text),
|
||||||
data: Mutex::new(style),
|
data: Mutex::new(style),
|
||||||
});
|
});
|
||||||
<Text as TextAspect>::add_node_members(node);
|
|
||||||
node.add_aspect_raw(text.clone());
|
node.add_aspect_raw(text.clone());
|
||||||
|
|
||||||
Ok(text)
|
Ok(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, token: &MainThreadToken) {
|
fn draw(&self, token: &MainThreadToken) {
|
||||||
let style =
|
let style = self.style.get_or_init(|| {
|
||||||
self.style
|
let font = self
|
||||||
.get_or_try_init(|| -> Result<SkTextStyle, color_eyre::eyre::Error> {
|
.font_path
|
||||||
let font = self
|
.as_deref()
|
||||||
.font_path
|
.and_then(|path| Font::from_file(path).ok())
|
||||||
.as_deref()
|
.unwrap_or_default();
|
||||||
.and_then(|path| Font::from_file(path).ok())
|
SkTextStyle::from_font(font, 1.0, Color32::WHITE)
|
||||||
.unwrap_or_default();
|
});
|
||||||
Ok(SkTextStyle::from_font(font, 1.0, Color32::WHITE))
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Ok(style) = style {
|
let text = self.text.lock();
|
||||||
let text = self.text.lock();
|
let data = self.data.lock();
|
||||||
let data = self.data.lock();
|
let transform = self.space.global_transform()
|
||||||
let transform = self.space.global_transform()
|
* Mat4::from_scale(vec3(
|
||||||
* Mat4::from_scale(vec3(
|
data.character_height,
|
||||||
data.character_height,
|
data.character_height,
|
||||||
data.character_height,
|
data.character_height,
|
||||||
data.character_height,
|
));
|
||||||
));
|
if let Some(bounds) = &data.bounds {
|
||||||
if let Some(bounds) = &data.bounds {
|
stereokit_rust::system::Text::add_in(
|
||||||
stereokit_rust::system::Text::add_in(
|
token,
|
||||||
token,
|
&*text,
|
||||||
&*text,
|
transform,
|
||||||
transform,
|
Vec2::from(bounds.bounds) / data.character_height,
|
||||||
Vec2::from(bounds.bounds) / data.character_height,
|
match bounds.fit {
|
||||||
match bounds.fit {
|
super::TextFit::Wrap => TextFit::Wrap,
|
||||||
super::TextFit::Wrap => TextFit::Wrap,
|
super::TextFit::Clip => TextFit::Clip,
|
||||||
super::TextFit::Clip => TextFit::Clip,
|
super::TextFit::Squeeze => TextFit::Squeeze,
|
||||||
super::TextFit::Squeeze => TextFit::Squeeze,
|
super::TextFit::Exact => TextFit::Exact,
|
||||||
super::TextFit::Exact => TextFit::Exact,
|
super::TextFit::Overflow => TextFit::Overflow,
|
||||||
super::TextFit::Overflow => TextFit::Overflow,
|
},
|
||||||
},
|
Some(*style),
|
||||||
Some(*style),
|
Some(Color128::new(
|
||||||
Some(Color128::new(
|
data.color.c.r,
|
||||||
data.color.c.r,
|
data.color.c.g,
|
||||||
data.color.c.g,
|
data.color.c.b,
|
||||||
data.color.c.b,
|
data.color.a,
|
||||||
data.color.a,
|
)),
|
||||||
)),
|
data.bounds
|
||||||
data.bounds
|
.as_ref()
|
||||||
.as_ref()
|
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
||||||
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
Some(convert_align(data.text_align_x, data.text_align_y)),
|
||||||
Some(convert_align(data.text_align_x, data.text_align_y)),
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
stereokit_rust::system::Text::add_at(
|
||||||
stereokit_rust::system::Text::add_at(
|
token,
|
||||||
token,
|
&*text,
|
||||||
&*text,
|
transform,
|
||||||
transform,
|
Some(*style),
|
||||||
Some(*style),
|
Some(Color128::new(
|
||||||
Some(Color128::new(
|
data.color.c.r,
|
||||||
data.color.c.r,
|
data.color.c.g,
|
||||||
data.color.c.g,
|
data.color.c.b,
|
||||||
data.color.c.b,
|
data.color.a,
|
||||||
data.color.a,
|
)),
|
||||||
)),
|
data.bounds
|
||||||
data.bounds
|
.as_ref()
|
||||||
.as_ref()
|
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
||||||
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
Some(convert_align(data.text_align_x, data.text_align_y)),
|
||||||
Some(convert_align(data.text_align_x, data.text_align_y)),
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for Text {
|
|
||||||
const NAME: &'static str = "Text";
|
|
||||||
}
|
|
||||||
impl TextAspect for Text {
|
impl TextAspect for Text {
|
||||||
fn set_character_height(
|
fn set_character_height(
|
||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
use super::alias::{Alias, AliasInfo};
|
use super::alias::{Alias, AliasInfo};
|
||||||
use super::spatial::{
|
use super::spatial::{
|
||||||
Spatial, SPATIAL_REF_GET_LOCAL_BOUNDING_BOX_SERVER_OPCODE,
|
SPATIAL_REF_GET_LOCAL_BOUNDING_BOX_SERVER_OPCODE,
|
||||||
SPATIAL_REF_GET_RELATIVE_BOUNDING_BOX_SERVER_OPCODE, SPATIAL_REF_GET_TRANSFORM_SERVER_OPCODE,
|
SPATIAL_REF_GET_RELATIVE_BOUNDING_BOX_SERVER_OPCODE, SPATIAL_REF_GET_TRANSFORM_SERVER_OPCODE,
|
||||||
|
Spatial,
|
||||||
};
|
};
|
||||||
use super::{Aspect, Node};
|
use super::{Aspect, AspectIdentifier, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::create_interface;
|
use crate::core::error::Result;
|
||||||
use crate::nodes::spatial::Transform;
|
|
||||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||||
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
||||||
use color_eyre::eyre::{OptionExt, Result};
|
use crate::nodes::spatial::Transform;
|
||||||
use glam::{vec2, vec3, vec3a, Vec3, Vec3A, Vec3Swizzles};
|
use color_eyre::eyre::OptionExt;
|
||||||
use once_cell::sync::Lazy;
|
use glam::{Vec3, Vec3A, Vec3Swizzles, vec2, vec3, vec3a};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stardust_xr::values::Vector3;
|
use stardust_xr::values::Vector3;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
// TODO: get SDFs working properly with non-uniform scale and so on, output distance relative to the spatial it's compared against
|
// TODO: get SDFs working properly with non-uniform scale and so on, output distance relative to the spatial it's compared against
|
||||||
|
|
||||||
pub static FIELD_ALIAS_INFO: Lazy<AliasInfo> = Lazy::new(|| AliasInfo {
|
pub static FIELD_ALIAS_INFO: LazyLock<AliasInfo> = LazyLock::new(|| AliasInfo {
|
||||||
server_methods: vec![
|
server_methods: vec![
|
||||||
SPATIAL_REF_GET_TRANSFORM_SERVER_OPCODE,
|
SPATIAL_REF_GET_TRANSFORM_SERVER_OPCODE,
|
||||||
SPATIAL_REF_GET_LOCAL_BOUNDING_BOX_SERVER_OPCODE,
|
SPATIAL_REF_GET_LOCAL_BOUNDING_BOX_SERVER_OPCODE,
|
||||||
@@ -145,15 +145,65 @@ impl Field {
|
|||||||
shape: Mutex::new(shape),
|
shape: Mutex::new(shape),
|
||||||
};
|
};
|
||||||
let field = node.add_aspect(field);
|
let field = node.add_aspect(field);
|
||||||
<Field as FieldRefAspect>::add_node_members(node);
|
node.add_aspect(FieldRef);
|
||||||
<Field as FieldAspect>::add_node_members(node);
|
|
||||||
Ok(field)
|
Ok(field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for Field {
|
impl AspectIdentifier for Field {
|
||||||
const NAME: &'static str = "Field";
|
impl_aspect_for_field_aspect_id! {}
|
||||||
}
|
}
|
||||||
impl FieldRefAspect for Field {
|
impl Aspect for Field {
|
||||||
|
impl_aspect_for_field_aspect! {}
|
||||||
|
}
|
||||||
|
impl FieldAspect for Field {
|
||||||
|
fn set_shape(node: Arc<Node>, _calling_client: Arc<Client>, shape: Shape) -> Result<()> {
|
||||||
|
let field = node.get_aspect::<Field>()?;
|
||||||
|
*field.shape.lock() = shape;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn export_field(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<u64> {
|
||||||
|
let id = rand::random();
|
||||||
|
EXPORTED_FIELDS.lock().insert(id, node);
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FieldTrait for Field {
|
||||||
|
fn spatial_ref(&self) -> &Spatial {
|
||||||
|
&self.spatial
|
||||||
|
}
|
||||||
|
fn local_distance(&self, p: Vec3A) -> f32 {
|
||||||
|
match self.shape.lock().clone() {
|
||||||
|
Shape::Box(size) => {
|
||||||
|
let q = vec3(
|
||||||
|
p.x.abs() - (size.x * 0.5_f32),
|
||||||
|
p.y.abs() - (size.y * 0.5_f32),
|
||||||
|
p.z.abs() - (size.z * 0.5_f32),
|
||||||
|
);
|
||||||
|
let v = vec3a(q.x.max(0_f32), q.y.max(0_f32), q.z.max(0_f32));
|
||||||
|
v.length() + q.x.max(q.y.max(q.z)).min(0_f32)
|
||||||
|
}
|
||||||
|
Shape::Cylinder(CylinderShape { length, radius }) => {
|
||||||
|
let d = vec2(p.xz().length().abs() - radius, p.y.abs() - (length * 0.5));
|
||||||
|
d.x.max(d.y).min(0.0) + d.max(vec2(0.0, 0.0)).length()
|
||||||
|
}
|
||||||
|
Shape::Sphere(radius) => p.length() - radius,
|
||||||
|
Shape::Torus(TorusShape { radius_a, radius_b }) => {
|
||||||
|
let q = vec2(p.xz().length() - radius_a, p.y);
|
||||||
|
q.length() - radius_b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FieldRef;
|
||||||
|
impl AspectIdentifier for FieldRef {
|
||||||
|
impl_aspect_for_field_ref_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for FieldRef {
|
||||||
|
impl_aspect_for_field_ref_aspect! {}
|
||||||
|
}
|
||||||
|
impl FieldRefAspect for FieldRef {
|
||||||
async fn distance(
|
async fn distance(
|
||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
@@ -205,56 +255,14 @@ impl FieldRefAspect for Field {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl FieldAspect for Field {
|
|
||||||
fn set_shape(node: Arc<Node>, _calling_client: Arc<Client>, shape: Shape) -> Result<()> {
|
|
||||||
let field = node.get_aspect::<Field>()?;
|
|
||||||
*field.shape.lock() = shape;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn export_field(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<u64> {
|
impl InterfaceAspect for Interface {
|
||||||
let id = rand::random();
|
|
||||||
EXPORTED_FIELDS.lock().insert(id, node);
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FieldTrait for Field {
|
|
||||||
fn spatial_ref(&self) -> &Spatial {
|
|
||||||
&self.spatial
|
|
||||||
}
|
|
||||||
fn local_distance(&self, p: Vec3A) -> f32 {
|
|
||||||
match self.shape.lock().clone() {
|
|
||||||
Shape::Box(size) => {
|
|
||||||
let q = vec3(
|
|
||||||
p.x.abs() - (size.x * 0.5_f32),
|
|
||||||
p.y.abs() - (size.y * 0.5_f32),
|
|
||||||
p.z.abs() - (size.z * 0.5_f32),
|
|
||||||
);
|
|
||||||
let v = vec3a(q.x.max(0_f32), q.y.max(0_f32), q.z.max(0_f32));
|
|
||||||
v.length() + q.x.max(q.y.max(q.z)).min(0_f32)
|
|
||||||
}
|
|
||||||
Shape::Cylinder(CylinderShape { length, radius }) => {
|
|
||||||
let d = vec2(p.xy().length().abs() - radius, p.z.abs() - (length * 0.5));
|
|
||||||
d.x.max(d.y).min(0.0) + d.max(vec2(0.0, 0.0)).length()
|
|
||||||
}
|
|
||||||
Shape::Sphere(radius) => p.length() - radius,
|
|
||||||
Shape::Torus(TorusShape { radius_a, radius_b }) => {
|
|
||||||
let q = vec2(p.xz().length() - radius_a, p.y);
|
|
||||||
q.length() - radius_b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
create_interface!(FieldInterface);
|
|
||||||
pub struct FieldInterface;
|
|
||||||
impl InterfaceAspect for FieldInterface {
|
|
||||||
async fn import_field_ref(
|
async fn import_field_ref(
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
uid: u64,
|
uid: u64,
|
||||||
) -> Result<Arc<Node>> {
|
) -> Result<Arc<Node>> {
|
||||||
EXPORTED_FIELDS
|
Ok(EXPORTED_FIELDS
|
||||||
.lock()
|
.lock()
|
||||||
.get(&uid)
|
.get(&uid)
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
@@ -266,7 +274,7 @@ impl InterfaceAspect for FieldInterface {
|
|||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
.ok_or_eyre("Couldn't find spatial with that ID")
|
.ok_or_eyre("Couldn't find spatial with that ID")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_field(
|
fn create_field(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::{Finger, Hand, InputDataTrait, InputHandler, InputMethod, Joint, Thumb};
|
use super::{Finger, Hand, InputDataTrait, InputHandler, InputMethod, Joint, Thumb};
|
||||||
use crate::nodes::fields::{Field, FieldTrait};
|
use crate::nodes::fields::{Field, FieldTrait};
|
||||||
use crate::nodes::spatial::Spatial;
|
use crate::nodes::spatial::Spatial;
|
||||||
use glam::{vec3a, Mat4, Quat};
|
use glam::{Mat4, Quat, vec3a};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
impl Default for Joint {
|
impl Default for Joint {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use super::{InputHandlerAspect, INPUT_HANDLER_REGISTRY, INPUT_METHOD_REGISTRY};
|
use super::{INPUT_HANDLER_REGISTRY, INPUT_METHOD_REGISTRY, InputHandlerAspect};
|
||||||
use crate::nodes::{alias::AliasList, fields::Field, spatial::Spatial, Aspect, Node};
|
use crate::nodes::{Node, alias::AliasList, fields::Field, spatial::Spatial};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -23,9 +23,6 @@ impl InputHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for InputHandler {
|
|
||||||
const NAME: &'static str = "InputHandler";
|
|
||||||
}
|
|
||||||
impl InputHandlerAspect for InputHandler {}
|
impl InputHandlerAspect for InputHandler {}
|
||||||
impl PartialEq for InputHandler {
|
impl PartialEq for InputHandler {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
use super::{
|
use super::{
|
||||||
input_method_client, InputData, InputDataTrait, InputDataType, InputHandler, InputMethodAspect,
|
INPUT_HANDLER_REGISTRY, INPUT_METHOD_REF_ASPECT_ALIAS_INFO, INPUT_METHOD_REGISTRY, InputData,
|
||||||
InputMethodRefAspect, INPUT_HANDLER_REGISTRY, INPUT_METHOD_REF_ASPECT_ALIAS_INFO,
|
InputDataTrait, InputDataType, InputHandler, InputMethodAspect, InputMethodRefAspect,
|
||||||
INPUT_METHOD_REGISTRY,
|
input_method_client,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, registry::Registry},
|
core::{
|
||||||
|
client::Client,
|
||||||
|
error::{Result, ServerError},
|
||||||
|
registry::Registry,
|
||||||
|
},
|
||||||
nodes::{
|
nodes::{
|
||||||
|
Node,
|
||||||
alias::{Alias, AliasList},
|
alias::{Alias, AliasList},
|
||||||
fields::{Field, FIELD_ALIAS_INFO},
|
fields::{FIELD_ALIAS_INFO, Field},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Aspect, Node,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::eyre;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
@@ -25,7 +29,7 @@ pub struct InputMethod {
|
|||||||
handler_aliases: AliasList,
|
handler_aliases: AliasList,
|
||||||
handler_field_aliases: AliasList,
|
handler_field_aliases: AliasList,
|
||||||
pub(super) handler_order: Mutex<Vec<Weak<InputHandler>>>,
|
pub(super) handler_order: Mutex<Vec<Weak<InputHandler>>>,
|
||||||
pub internal_capture_requests: Registry<InputHandler>,
|
pub capture_attempts: Registry<InputHandler>,
|
||||||
pub captures: Registry<InputHandler>,
|
pub captures: Registry<InputHandler>,
|
||||||
}
|
}
|
||||||
impl InputMethod {
|
impl InputMethod {
|
||||||
@@ -42,16 +46,15 @@ impl InputMethod {
|
|||||||
handler_aliases: AliasList::default(),
|
handler_aliases: AliasList::default(),
|
||||||
handler_field_aliases: AliasList::default(),
|
handler_field_aliases: AliasList::default(),
|
||||||
handler_order: Mutex::new(Vec::new()),
|
handler_order: Mutex::new(Vec::new()),
|
||||||
internal_capture_requests: Registry::new(),
|
capture_attempts: Registry::new(),
|
||||||
captures: Registry::new(),
|
captures: Registry::new(),
|
||||||
};
|
};
|
||||||
<InputMethod as InputMethodRefAspect>::add_node_members(node);
|
|
||||||
<InputMethod as InputMethodAspect>::add_node_members(node);
|
|
||||||
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
|
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
|
||||||
method.handle_new_handler(&handler);
|
method.handle_new_handler(&handler);
|
||||||
}
|
}
|
||||||
let method = INPUT_METHOD_REGISTRY.add(method);
|
let method = INPUT_METHOD_REGISTRY.add(method);
|
||||||
node.add_aspect_raw(method.clone());
|
node.add_aspect_raw(method.clone());
|
||||||
|
node.add_aspect(InputMethodRef);
|
||||||
Ok(method)
|
Ok(method)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +134,7 @@ impl InputMethod {
|
|||||||
self.handler_aliases.remove_aspect(handler);
|
self.handler_aliases.remove_aspect(handler);
|
||||||
self.handler_field_aliases
|
self.handler_field_aliases
|
||||||
.remove_aspect(handler.field.as_ref());
|
.remove_aspect(handler.field.as_ref());
|
||||||
|
self.capture_attempts.remove(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn serialize(&self, alias_id: u64, handler: &Arc<InputHandler>) -> InputData {
|
pub(super) fn serialize(&self, alias_id: u64, handler: &Arc<InputHandler>) -> InputData {
|
||||||
@@ -153,24 +157,23 @@ impl InputMethod {
|
|||||||
captured: self.captures.get_valid_contents().contains(handler),
|
captured: self.captures.get_valid_contents().contains(handler),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl Aspect for InputMethod {
|
|
||||||
const NAME: &'static str = "InputMethod";
|
|
||||||
}
|
|
||||||
impl InputMethodRefAspect for InputMethod {
|
|
||||||
#[doc = "Have the input handler that this method reference came from capture the method for the next frame."]
|
|
||||||
fn request_capture(
|
|
||||||
node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
handler: Arc<Node>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let input_method = node.get_aspect::<InputMethod>()?;
|
|
||||||
let input_handler = handler.get_aspect::<InputHandler>()?;
|
|
||||||
|
|
||||||
input_method
|
pub(super) fn cull_capture_attempts(&self) {
|
||||||
.internal_capture_requests
|
let sent = self
|
||||||
.add_raw(&input_handler);
|
.handler_order
|
||||||
Ok(())
|
.lock()
|
||||||
|
.iter()
|
||||||
|
.filter_map(Weak::upgrade)
|
||||||
|
.collect::<Registry<InputHandler>>();
|
||||||
|
self.capture_attempts.retain(|handler| {
|
||||||
|
!handler
|
||||||
|
.spatial
|
||||||
|
.node()
|
||||||
|
.and_then(|n| n.get_client())
|
||||||
|
.map(|c| c.unresponsive())
|
||||||
|
.unwrap_or(false)
|
||||||
|
&& sent.contains(handler)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InputMethodAspect for InputMethod {
|
impl InputMethodAspect for InputMethod {
|
||||||
@@ -231,3 +234,46 @@ impl Drop for InputMethod {
|
|||||||
INPUT_METHOD_REGISTRY.remove(self);
|
INPUT_METHOD_REGISTRY.remove(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InputMethodRef;
|
||||||
|
impl InputMethodRefAspect for InputMethodRef {
|
||||||
|
#[doc = "Try to capture the input method with the given handler. When the handler does not get input from the method, it will be released."]
|
||||||
|
fn try_capture(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
handler: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let input_method = node.get_aspect::<InputMethod>()?;
|
||||||
|
let input_handler = handler.get_aspect::<InputHandler>()?;
|
||||||
|
|
||||||
|
input_method.capture_attempts.add_raw(&input_handler);
|
||||||
|
|
||||||
|
let Some(handler_alias) = input_method
|
||||||
|
.handler_aliases
|
||||||
|
.get_from_aspect(&*input_handler)
|
||||||
|
else {
|
||||||
|
return Err(ServerError::Report(eyre!(
|
||||||
|
"Internal: Couldn't get handler alias somehow?"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
input_method_client::request_capture_handler(&node, handler_alias.get_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "If captured by this handler, release it (e.g. the object is let go of after grabbing)."]
|
||||||
|
fn release(node: Arc<Node>, _calling_client: Arc<Client>, handler: Arc<Node>) -> Result<()> {
|
||||||
|
let input_method = node.get_aspect::<InputMethod>()?;
|
||||||
|
let input_handler = handler.get_aspect::<InputHandler>()?;
|
||||||
|
|
||||||
|
input_method.capture_attempts.remove(&input_handler);
|
||||||
|
|
||||||
|
let Some(handler_alias) = input_method
|
||||||
|
.handler_aliases
|
||||||
|
.get_from_aspect(&*input_handler)
|
||||||
|
else {
|
||||||
|
return Err(ServerError::Report(eyre!(
|
||||||
|
"Internal: Couldn't get handler alias somehow?"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
input_method_client::release_handler(&node, handler_alias.get_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ mod tip;
|
|||||||
pub use handler::*;
|
pub use handler::*;
|
||||||
pub use method::*;
|
pub use method::*;
|
||||||
|
|
||||||
|
use super::Aspect;
|
||||||
|
use super::AspectIdentifier;
|
||||||
use super::fields::Field;
|
use super::fields::Field;
|
||||||
use super::spatial::Spatial;
|
use super::spatial::Spatial;
|
||||||
use crate::create_interface;
|
use crate::core::error::Result;
|
||||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||||
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
||||||
use crate::{core::client::Client, nodes::Node};
|
use crate::{core::client::Client, nodes::Node};
|
||||||
use crate::{core::registry::Registry, nodes::spatial::Transform};
|
use crate::{core::registry::Registry, nodes::spatial::Transform};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -25,6 +26,25 @@ pub static INPUT_HANDLER_REGISTRY: Registry<InputHandler> = Registry::new();
|
|||||||
|
|
||||||
stardust_xr_server_codegen::codegen_input_protocol!();
|
stardust_xr_server_codegen::codegen_input_protocol!();
|
||||||
|
|
||||||
|
impl AspectIdentifier for InputHandler {
|
||||||
|
impl_aspect_for_input_handler_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for InputHandler {
|
||||||
|
impl_aspect_for_input_handler_aspect! {}
|
||||||
|
}
|
||||||
|
impl AspectIdentifier for InputMethod {
|
||||||
|
impl_aspect_for_input_method_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for InputMethod {
|
||||||
|
impl_aspect_for_input_method_aspect! {}
|
||||||
|
}
|
||||||
|
impl AspectIdentifier for InputMethodRef {
|
||||||
|
impl_aspect_for_input_method_ref_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for InputMethodRef {
|
||||||
|
impl_aspect_for_input_method_ref_aspect! {}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait InputDataTrait {
|
pub trait InputDataTrait {
|
||||||
fn transform(&mut self, method: &InputMethod, handler: &InputHandler);
|
fn transform(&mut self, method: &InputMethod, handler: &InputHandler);
|
||||||
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
|
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
|
||||||
@@ -47,9 +67,7 @@ impl InputDataTrait for InputDataType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_interface!(InputInterface);
|
impl InterfaceAspect for Interface {
|
||||||
pub struct InputInterface;
|
|
||||||
impl InterfaceAspect for InputInterface {
|
|
||||||
#[doc = "Create an input method node"]
|
#[doc = "Create an input method node"]
|
||||||
fn create_input_method(
|
fn create_input_method(
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
@@ -147,6 +165,6 @@ pub fn process_input() {
|
|||||||
let _ = input_handler_client::input(&handler_node, &methods, &datas);
|
let _ = input_handler_client::input(&handler_node, &methods, &datas);
|
||||||
}
|
}
|
||||||
for method in methods {
|
for method in methods {
|
||||||
method.internal_capture_requests.clear();
|
method.cull_capture_attempts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::nodes::{
|
|||||||
fields::{Field, FieldTrait, Ray, RayMarchResult},
|
fields::{Field, FieldTrait, Ray, RayMarchResult},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
};
|
};
|
||||||
use glam::{vec3, Mat4, Quat};
|
use glam::{Mat4, Quat, vec3};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
impl Default for Pointer {
|
impl Default for Pointer {
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
use super::{
|
use super::{Item, ItemType, create_item_acceptor_flex, register_item_ui_flex};
|
||||||
create_item_acceptor_flex, register_item_ui_flex, Item, ItemAcceptor, ItemInterface, ItemType,
|
use crate::bail;
|
||||||
};
|
use crate::core::error::Result;
|
||||||
|
use crate::nodes::Aspect;
|
||||||
|
use crate::nodes::AspectIdentifier;
|
||||||
use crate::nodes::items::ITEM_ACCEPTOR_ASPECT_ALIAS_INFO;
|
use crate::nodes::items::ITEM_ACCEPTOR_ASPECT_ALIAS_INFO;
|
||||||
use crate::nodes::items::ITEM_ASPECT_ALIAS_INFO;
|
use crate::nodes::items::ITEM_ASPECT_ALIAS_INFO;
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, registry::Registry, scenegraph::MethodResponseSender},
|
core::{client::Client, registry::Registry, scenegraph::MethodResponseSender},
|
||||||
create_interface,
|
|
||||||
nodes::{
|
nodes::{
|
||||||
|
Message, Node,
|
||||||
drawable::{
|
drawable::{
|
||||||
model::{MaterialWrapper, ModelPart},
|
model::{MaterialWrapper, ModelPart},
|
||||||
shaders::UNLIT_SHADER_BYTES,
|
shaders::UNLIT_SHADER_BYTES,
|
||||||
},
|
},
|
||||||
items::TypeInfo,
|
items::TypeInfo,
|
||||||
spatial::{Spatial, Transform},
|
spatial::{Spatial, Transform},
|
||||||
Message, Node,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, eyre, Result};
|
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mint::{ColumnMatrix4, Vector2};
|
use mint::{ColumnMatrix4, Vector2};
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::OnceLock;
|
||||||
use stereokit_rust::{
|
use stereokit_rust::{
|
||||||
material::{Material, Transparency},
|
material::{Material, Transparency},
|
||||||
shader::Shader,
|
shader::Shader,
|
||||||
@@ -33,7 +33,6 @@ use stereokit_rust::{
|
|||||||
tex::{Tex, TexFormat, TexType},
|
tex::{Tex, TexFormat, TexType},
|
||||||
util::Color128,
|
util::Color128,
|
||||||
};
|
};
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
pub struct TexWrapper(pub Tex);
|
pub struct TexWrapper(pub Tex);
|
||||||
unsafe impl Send for TexWrapper {}
|
unsafe impl Send for TexWrapper {}
|
||||||
@@ -48,6 +47,12 @@ lazy_static! {
|
|||||||
ui: Default::default(),
|
ui: Default::default(),
|
||||||
items: Registry::new(),
|
items: Registry::new(),
|
||||||
acceptors: Registry::new(),
|
acceptors: Registry::new(),
|
||||||
|
add_acceptor_aspect: |node| {
|
||||||
|
node.add_aspect(CameraItemAcceptor);
|
||||||
|
},
|
||||||
|
add_ui_aspect: |node| {
|
||||||
|
node.add_aspect(CameraItemUi);
|
||||||
|
},
|
||||||
new_acceptor_fn: |node, acceptor, acceptor_field| {
|
new_acceptor_fn: |node, acceptor, acceptor_field| {
|
||||||
let _ = camera_item_ui_client::create_acceptor(node, acceptor, acceptor_field);
|
let _ = camera_item_ui_client::create_acceptor(node, acceptor, acceptor_field);
|
||||||
}
|
}
|
||||||
@@ -62,30 +67,27 @@ struct FrameInfo {
|
|||||||
pub struct CameraItem {
|
pub struct CameraItem {
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
frame_info: Mutex<FrameInfo>,
|
frame_info: Mutex<FrameInfo>,
|
||||||
sk_tex: OnceCell<TexWrapper>,
|
sk_tex: OnceLock<TexWrapper>,
|
||||||
sk_mat: OnceCell<Arc<MaterialWrapper>>,
|
sk_mat: OnceLock<Arc<MaterialWrapper>>,
|
||||||
applied_to: Registry<ModelPart>,
|
applied_to: Registry<ModelPart>,
|
||||||
apply_to: Registry<ModelPart>,
|
apply_to: Registry<ModelPart>,
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
impl CameraItem {
|
impl CameraItem {
|
||||||
pub fn add_to(node: &Arc<Node>, proj_matrix: Mat4, px_size: Vector2<u32>) {
|
pub fn add_to(node: &Arc<Node>, proj_matrix: Mat4, px_size: Vector2<u32>) {
|
||||||
Item::add_to(
|
let item = Arc::new(CameraItem {
|
||||||
node,
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
&ITEM_TYPE_INFO_CAMERA,
|
frame_info: Mutex::new(FrameInfo {
|
||||||
ItemType::Camera(CameraItem {
|
proj_matrix,
|
||||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
px_size,
|
||||||
frame_info: Mutex::new(FrameInfo {
|
|
||||||
proj_matrix,
|
|
||||||
px_size,
|
|
||||||
}),
|
|
||||||
sk_tex: OnceCell::new(),
|
|
||||||
sk_mat: OnceCell::new(),
|
|
||||||
applied_to: Registry::new(),
|
|
||||||
apply_to: Registry::new(),
|
|
||||||
}),
|
}),
|
||||||
);
|
sk_tex: OnceLock::new(),
|
||||||
// <CameraItem as CameraItemAspect>::node_methods(node);
|
sk_mat: OnceLock::new(),
|
||||||
|
applied_to: Registry::new(),
|
||||||
|
apply_to: Registry::new(),
|
||||||
|
});
|
||||||
|
Item::add_to(node, &ITEM_TYPE_INFO_CAMERA, ItemType::Camera(item.clone()));
|
||||||
|
node.add_aspect_raw(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame_flex(
|
fn frame_flex(
|
||||||
@@ -97,7 +99,7 @@ impl CameraItem {
|
|||||||
response.wrap_sync(move || {
|
response.wrap_sync(move || {
|
||||||
let ItemType::Camera(_camera) = &node.get_aspect::<Item>().unwrap().specialization
|
let ItemType::Camera(_camera) = &node.get_aspect::<Item>().unwrap().specialization
|
||||||
else {
|
else {
|
||||||
return Err(eyre!("Wrong item type?"));
|
bail!("Wrong item type?");
|
||||||
};
|
};
|
||||||
Ok(serialize(())?.into())
|
Ok(serialize(())?.into())
|
||||||
});
|
});
|
||||||
@@ -109,7 +111,7 @@ impl CameraItem {
|
|||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let ItemType::Camera(camera) = &node.get_aspect::<Item>().unwrap().specialization else {
|
let ItemType::Camera(camera) = &node.get_aspect::<Item>().unwrap().specialization else {
|
||||||
bail!("Wrong item type?")
|
bail!("Wrong item type?");
|
||||||
};
|
};
|
||||||
let model_part_node =
|
let model_part_node =
|
||||||
calling_client.get_node("Model part", deserialize(&message.data).unwrap())?;
|
calling_client.get_node("Model part", deserialize(&message.data).unwrap())?;
|
||||||
@@ -137,19 +139,13 @@ impl CameraItem {
|
|||||||
TexFormat::RGBA32Linear,
|
TexFormat::RGBA32Linear,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
let sk_mat = self
|
let sk_mat = self.sk_mat.get_or_init(|| {
|
||||||
.sk_mat
|
let shader = Shader::from_memory(UNLIT_SHADER_BYTES).unwrap();
|
||||||
.get_or_try_init(|| -> Result<Arc<MaterialWrapper>> {
|
let mut mat = Material::new(&shader, None);
|
||||||
let shader = Shader::from_memory(UNLIT_SHADER_BYTES)?;
|
mat.get_all_param_info().set_texture("diffuse", &sk_tex.0);
|
||||||
let mut mat = Material::new(&shader, None);
|
mat.transparency(Transparency::Blend);
|
||||||
mat.get_all_param_info().set_texture("diffuse", &sk_tex.0);
|
Arc::new(MaterialWrapper(mat))
|
||||||
mat.transparency(Transparency::Blend);
|
});
|
||||||
Ok(Arc::new(MaterialWrapper(mat)))
|
|
||||||
});
|
|
||||||
let Ok(sk_mat) = sk_mat else {
|
|
||||||
error!("unable to make camera item stereokit texture");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
for model_part in self.apply_to.take_valid_contents() {
|
for model_part in self.apply_to.take_valid_contents() {
|
||||||
model_part.replace_material(sk_mat.clone())
|
model_part.replace_material(sk_mat.clone())
|
||||||
}
|
}
|
||||||
@@ -167,9 +163,31 @@ impl CameraItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for CameraItem {
|
||||||
|
impl_aspect_for_camera_item_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for CameraItem {
|
||||||
|
impl_aspect_for_camera_item_aspect! {}
|
||||||
|
}
|
||||||
impl CameraItemAspect for CameraItem {}
|
impl CameraItemAspect for CameraItem {}
|
||||||
|
|
||||||
impl CameraItemAcceptorAspect for ItemAcceptor {
|
pub struct CameraItemUi;
|
||||||
|
impl AspectIdentifier for CameraItemUi {
|
||||||
|
impl_aspect_for_camera_item_ui_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for CameraItemUi {
|
||||||
|
impl_aspect_for_camera_item_ui_aspect! {}
|
||||||
|
}
|
||||||
|
impl CameraItemUiAspect for CameraItemUi {}
|
||||||
|
|
||||||
|
pub struct CameraItemAcceptor;
|
||||||
|
impl AspectIdentifier for CameraItemAcceptor {
|
||||||
|
impl_aspect_for_camera_item_acceptor_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for CameraItemAcceptor {
|
||||||
|
impl_aspect_for_camera_item_acceptor_aspect! {}
|
||||||
|
}
|
||||||
|
impl CameraItemAcceptorAspect for CameraItemAcceptor {
|
||||||
fn capture_item(node: Arc<Node>, _calling_client: Arc<Client>, item: Arc<Node>) -> Result<()> {
|
fn capture_item(node: Arc<Node>, _calling_client: Arc<Client>, item: Arc<Node>) -> Result<()> {
|
||||||
super::acceptor_capture_item_flex(node, item)
|
super::acceptor_capture_item_flex(node, item)
|
||||||
}
|
}
|
||||||
@@ -184,8 +202,7 @@ pub fn update(token: &MainThreadToken) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_interface!(ItemInterface);
|
impl InterfaceAspect for Interface {
|
||||||
impl InterfaceAspect for ItemInterface {
|
|
||||||
#[doc = "Create a camera item at a specific location"]
|
#[doc = "Create a camera item at a specific location"]
|
||||||
fn create_camera_item(
|
fn create_camera_item(
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
@@ -206,19 +223,21 @@ impl InterfaceAspect for ItemInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "Register this client to manage camera items and create default 3D UI for them."]
|
#[doc = "Register this client to manage camera items and create default 3D UI for them."]
|
||||||
fn register_camera_item_ui(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
|
fn register_camera_item_ui(node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
|
||||||
|
node.add_aspect(CameraItemUi);
|
||||||
register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_CAMERA)
|
register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_CAMERA)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/camera/acceptor/<name>`."]
|
#[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/camera/acceptor/<name>`."]
|
||||||
fn create_camera_item_acceptor(
|
fn create_camera_item_acceptor(
|
||||||
_node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
id: u64,
|
id: u64,
|
||||||
parent: Arc<Node>,
|
parent: Arc<Node>,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
field: Arc<Node>,
|
field: Arc<Node>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
node.add_aspect(CameraItemAcceptor);
|
||||||
create_item_acceptor_flex(
|
create_item_acceptor_flex(
|
||||||
calling_client,
|
calling_client,
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -4,15 +4,16 @@ pub mod panel;
|
|||||||
use self::camera::CameraItem;
|
use self::camera::CameraItem;
|
||||||
use self::panel::PanelItemTrait;
|
use self::panel::PanelItemTrait;
|
||||||
use super::alias::AliasList;
|
use super::alias::AliasList;
|
||||||
use super::fields::{Field, FIELD_ALIAS_INFO};
|
use super::fields::{FIELD_ALIAS_INFO, Field};
|
||||||
use super::spatial::Spatial;
|
use super::spatial::Spatial;
|
||||||
use super::{Alias, Aspect, Node};
|
use super::{Alias, Aspect, AspectIdentifier, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
|
use crate::core::error::Result;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
|
use crate::ensure;
|
||||||
use crate::nodes::alias::AliasInfo;
|
use crate::nodes::alias::AliasInfo;
|
||||||
use crate::nodes::spatial::Transform;
|
|
||||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use crate::nodes::spatial::Transform;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
@@ -47,6 +48,8 @@ pub struct TypeInfo {
|
|||||||
pub ui: Mutex<Weak<ItemUI>>,
|
pub ui: Mutex<Weak<ItemUI>>,
|
||||||
pub items: Registry<Item>,
|
pub items: Registry<Item>,
|
||||||
pub acceptors: Registry<ItemAcceptor>,
|
pub acceptors: Registry<ItemAcceptor>,
|
||||||
|
pub add_ui_aspect: fn(node: &Node),
|
||||||
|
pub add_acceptor_aspect: fn(node: &Node),
|
||||||
pub new_acceptor_fn: fn(node: &Node, acceptor: &Arc<Node>, acceptor_field: &Arc<Node>),
|
pub new_acceptor_fn: fn(node: &Node, acceptor: &Arc<Node>, acceptor_field: &Arc<Node>),
|
||||||
}
|
}
|
||||||
impl Hash for TypeInfo {
|
impl Hash for TypeInfo {
|
||||||
@@ -81,7 +84,6 @@ impl Item {
|
|||||||
};
|
};
|
||||||
let item = type_info.items.add(item);
|
let item = type_info.items.add(item);
|
||||||
|
|
||||||
<Item as ItemAspect>::add_node_members(node);
|
|
||||||
if let Some(ui) = type_info.ui.lock().upgrade() {
|
if let Some(ui) = type_info.ui.lock().upgrade() {
|
||||||
ui.handle_create_item(&item);
|
ui.handle_create_item(&item);
|
||||||
}
|
}
|
||||||
@@ -108,8 +110,11 @@ impl Item {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for Item {
|
||||||
|
impl_aspect_for_item_aspect_id! {}
|
||||||
|
}
|
||||||
impl Aspect for Item {
|
impl Aspect for Item {
|
||||||
const NAME: &'static str = "Item";
|
impl_aspect_for_item_aspect! {}
|
||||||
}
|
}
|
||||||
impl ItemAspect for Item {
|
impl ItemAspect for Item {
|
||||||
fn release(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
fn release(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
@@ -129,7 +134,7 @@ impl Drop for Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum ItemType {
|
pub enum ItemType {
|
||||||
Camera(CameraItem),
|
Camera(Arc<CameraItem>),
|
||||||
Panel(Arc<dyn PanelItemTrait>),
|
Panel(Arc<dyn PanelItemTrait>),
|
||||||
}
|
}
|
||||||
impl ItemType {
|
impl ItemType {
|
||||||
@@ -284,8 +289,11 @@ impl ItemUI {
|
|||||||
.remove_aspect(acceptor.field.as_ref());
|
.remove_aspect(acceptor.field.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for ItemUI {
|
||||||
|
impl_aspect_for_item_ui_aspect_id! {}
|
||||||
|
}
|
||||||
impl Aspect for ItemUI {
|
impl Aspect for ItemUI {
|
||||||
const NAME: &'static str = "Item";
|
impl_aspect_for_item_ui_aspect! {}
|
||||||
}
|
}
|
||||||
impl Drop for ItemUI {
|
impl Drop for ItemUI {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -342,8 +350,11 @@ impl ItemAcceptor {
|
|||||||
let _ = item_acceptor_client::release_item(&node, alias.id);
|
let _ = item_acceptor_client::release_item(&node, alias.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for ItemAcceptor {
|
||||||
|
impl_aspect_for_item_acceptor_aspect_id! {}
|
||||||
|
}
|
||||||
impl Aspect for ItemAcceptor {
|
impl Aspect for ItemAcceptor {
|
||||||
const NAME: &'static str = "ItemAcceptor";
|
impl_aspect_for_item_acceptor_aspect! {}
|
||||||
}
|
}
|
||||||
impl ItemAcceptorAspect for ItemAcceptor {}
|
impl ItemAcceptorAspect for ItemAcceptor {}
|
||||||
impl Drop for ItemAcceptor {
|
impl Drop for ItemAcceptor {
|
||||||
@@ -364,6 +375,7 @@ pub fn register_item_ui_flex(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let ui = Node::from_id(&calling_client, type_info.ui_node_id, true).add_to_scenegraph()?;
|
let ui = Node::from_id(&calling_client, type_info.ui_node_id, true).add_to_scenegraph()?;
|
||||||
ItemUI::add_to(&ui, type_info)?;
|
ItemUI::add_to(&ui, type_info)?;
|
||||||
|
(type_info.add_ui_aspect)(&ui);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn create_item_acceptor_flex(
|
fn create_item_acceptor_flex(
|
||||||
@@ -381,6 +393,7 @@ fn create_item_acceptor_flex(
|
|||||||
let node = Node::from_id(&calling_client, id, true).add_to_scenegraph()?;
|
let node = Node::from_id(&calling_client, id, true).add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, Some(space.clone()), transform, false);
|
Spatial::add_to(&node, Some(space.clone()), transform, false);
|
||||||
ItemAcceptor::add_to(&node, type_info, field);
|
ItemAcceptor::add_to(&node, type_info, field);
|
||||||
|
(type_info.add_acceptor_aspect)(&node);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,6 +404,3 @@ fn acceptor_capture_item_flex(node: Arc<Node>, item: Arc<Node>) -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ItemInterface;
|
|
||||||
// create_interface!(ItemInterface);
|
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
use super::{create_item_acceptor_flex, register_item_ui_flex, ItemAcceptor, ItemInterface};
|
use super::camera::CameraItemAcceptor;
|
||||||
|
use super::{create_item_acceptor_flex, register_item_ui_flex};
|
||||||
|
use crate::bail;
|
||||||
|
use crate::core::error::Result;
|
||||||
use crate::nodes::items::ITEM_ACCEPTOR_ASPECT_ALIAS_INFO;
|
use crate::nodes::items::ITEM_ACCEPTOR_ASPECT_ALIAS_INFO;
|
||||||
use crate::nodes::items::ITEM_ASPECT_ALIAS_INFO;
|
use crate::nodes::items::ITEM_ASPECT_ALIAS_INFO;
|
||||||
|
use crate::nodes::{Aspect, AspectIdentifier};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
client::{get_env, state, Client, INTERNAL_CLIENT},
|
client::{Client, INTERNAL_CLIENT, get_env, state},
|
||||||
registry::Registry,
|
registry::Registry,
|
||||||
},
|
},
|
||||||
create_interface,
|
|
||||||
nodes::{
|
nodes::{
|
||||||
|
Node,
|
||||||
drawable::model::ModelPart,
|
drawable::model::ModelPart,
|
||||||
items::{Item, ItemType, TypeInfo},
|
items::{Item, ItemType, TypeInfo},
|
||||||
spatial::{Spatial, Transform},
|
spatial::{Spatial, Transform},
|
||||||
Node,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use slotmap::{DefaultKey, Key, KeyData, SlotMap};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
@@ -32,6 +36,7 @@ impl Default for Geometry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
pub static ref KEYMAPS: Mutex<SlotMap<DefaultKey, String>> = Mutex::new(SlotMap::default());
|
||||||
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
||||||
type_name: "panel",
|
type_name: "panel",
|
||||||
alias_info: PANEL_ITEM_ASPECT_ALIAS_INFO.clone(),
|
alias_info: PANEL_ITEM_ASPECT_ALIAS_INFO.clone(),
|
||||||
@@ -39,6 +44,12 @@ lazy_static! {
|
|||||||
ui: Default::default(),
|
ui: Default::default(),
|
||||||
items: Registry::new(),
|
items: Registry::new(),
|
||||||
acceptors: Registry::new(),
|
acceptors: Registry::new(),
|
||||||
|
add_acceptor_aspect: |node| {
|
||||||
|
node.add_aspect(PanelItemUi);
|
||||||
|
},
|
||||||
|
add_ui_aspect: |node| {
|
||||||
|
node.add_aspect(PanelItemAcceptor);
|
||||||
|
},
|
||||||
new_acceptor_fn: |node, acceptor, acceptor_field| {
|
new_acceptor_fn: |node, acceptor, acceptor_field| {
|
||||||
let _ = panel_item_ui_client::create_acceptor(node, acceptor, acceptor_field);
|
let _ = panel_item_ui_client::create_acceptor(node, acceptor, acceptor_field);
|
||||||
}
|
}
|
||||||
@@ -65,7 +76,7 @@ pub trait Backend: Send + Sync + 'static {
|
|||||||
scroll_steps: Option<Vector2<f32>>,
|
scroll_steps: Option<Vector2<f32>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn keyboard_keys(&self, surface: &SurfaceId, keymap_id: u64, keys: Vec<i32>);
|
fn keyboard_key(&self, surface: &SurfaceId, keymap_id: u64, key: u32, pressed: bool);
|
||||||
|
|
||||||
fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>);
|
fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>);
|
||||||
fn touch_move(&self, id: u32, position: Vector2<f32>);
|
fn touch_move(&self, id: u32, position: Vector2<f32>);
|
||||||
@@ -115,7 +126,7 @@ impl<B: Backend> PanelItem<B> {
|
|||||||
&ITEM_TYPE_INFO_PANEL,
|
&ITEM_TYPE_INFO_PANEL,
|
||||||
ItemType::Panel(generic_panel_item),
|
ItemType::Panel(generic_panel_item),
|
||||||
);
|
);
|
||||||
<Self as PanelItemAspect>::add_node_members(&node);
|
node.add_aspect_raw(panel_item.clone());
|
||||||
|
|
||||||
(node, panel_item)
|
(node, panel_item)
|
||||||
}
|
}
|
||||||
@@ -197,9 +208,12 @@ impl<B: Backend> PanelItem<B> {
|
|||||||
panel_item_client::destroy_child(&node, id);
|
panel_item_client::destroy_child(&node, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<B: Backend> AspectIdentifier for PanelItem<B> {
|
||||||
// make these stupid vectors u32 in the protocol somehow!!!!!!!1
|
impl_aspect_for_panel_item_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl<B: Backend> Aspect for PanelItem<B> {
|
||||||
|
impl_aspect_for_panel_item_aspect! {}
|
||||||
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
impl<B: Backend> PanelItemAspect for PanelItem<B> {
|
impl<B: Backend> PanelItemAspect for PanelItem<B> {
|
||||||
#[doc = "Apply the cursor as a material to a model."]
|
#[doc = "Apply the cursor as a material to a model."]
|
||||||
@@ -341,19 +355,20 @@ impl<B: Backend> PanelItemAspect for PanelItem<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "Send a series of key presses and releases (positive keycode for pressed, negative for released)."]
|
#[doc = "Send a series of key presses and releases (positive keycode for pressed, negative for released)."]
|
||||||
fn keyboard_keys(
|
fn keyboard_key(
|
||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
surface: SurfaceId,
|
surface: SurfaceId,
|
||||||
keymap_id: u64,
|
keymap_id: u64,
|
||||||
keys: Vec<i32>,
|
key: u32,
|
||||||
|
pressed: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(&node) else {
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
panel_item
|
panel_item
|
||||||
.backend()
|
.backend()
|
||||||
.keyboard_keys(&surface, keymap_id, keys);
|
.keyboard_key(&surface, keymap_id, key, pressed);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +420,23 @@ impl<B: Backend> PanelItemAspect for PanelItem<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PanelItemAcceptorAspect for ItemAcceptor {
|
pub struct PanelItemUi;
|
||||||
|
impl AspectIdentifier for PanelItemUi {
|
||||||
|
impl_aspect_for_panel_item_ui_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for PanelItemUi {
|
||||||
|
impl_aspect_for_panel_item_ui_aspect! {}
|
||||||
|
}
|
||||||
|
impl PanelItemUiAspect for PanelItemUi {}
|
||||||
|
|
||||||
|
pub struct PanelItemAcceptor;
|
||||||
|
impl AspectIdentifier for PanelItemAcceptor {
|
||||||
|
impl_aspect_for_panel_item_acceptor_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for PanelItemAcceptor {
|
||||||
|
impl_aspect_for_panel_item_acceptor_aspect! {}
|
||||||
|
}
|
||||||
|
impl PanelItemAcceptorAspect for PanelItemAcceptor {
|
||||||
fn capture_item(node: Arc<Node>, _calling_client: Arc<Client>, item: Arc<Node>) -> Result<()> {
|
fn capture_item(node: Arc<Node>, _calling_client: Arc<Client>, item: Arc<Node>) -> Result<()> {
|
||||||
super::acceptor_capture_item_flex(node, item)
|
super::acceptor_capture_item_flex(node, item)
|
||||||
}
|
}
|
||||||
@@ -435,22 +466,23 @@ impl<B: Backend> Drop for PanelItem<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_interface!(ItemInterface);
|
impl InterfaceAspect for Interface {
|
||||||
impl InterfaceAspect for ItemInterface {
|
|
||||||
#[doc = "Register this client to manage the items of a certain type and create default 3D UI for them."]
|
#[doc = "Register this client to manage the items of a certain type and create default 3D UI for them."]
|
||||||
fn register_panel_item_ui(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
|
fn register_panel_item_ui(node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
|
||||||
|
node.add_aspect(CameraItemAcceptor);
|
||||||
register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_PANEL)
|
register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_PANEL)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/<item_type>/acceptor/<name>`."]
|
#[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/<item_type>/acceptor/<name>`."]
|
||||||
fn create_panel_item_acceptor(
|
fn create_panel_item_acceptor(
|
||||||
_node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
id: u64,
|
id: u64,
|
||||||
parent: Arc<Node>,
|
parent: Arc<Node>,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
field: Arc<Node>,
|
field: Arc<Node>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
node.add_aspect(PanelItemAcceptor);
|
||||||
create_item_acceptor_flex(
|
create_item_acceptor_flex(
|
||||||
calling_client,
|
calling_client,
|
||||||
id,
|
id,
|
||||||
@@ -460,4 +492,36 @@ impl InterfaceAspect for ItemInterface {
|
|||||||
field,
|
field,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn register_keymap(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
keymap: String,
|
||||||
|
) -> Result<u64> {
|
||||||
|
let mut keymaps = KEYMAPS.lock();
|
||||||
|
if let Some(found_keymap_id) = keymaps
|
||||||
|
.iter()
|
||||||
|
.filter(|(_k, v)| *v == &keymap)
|
||||||
|
.map(|(k, _v)| k)
|
||||||
|
.last()
|
||||||
|
{
|
||||||
|
return Ok(found_keymap_id.data().as_ffi());
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = keymaps.insert(keymap);
|
||||||
|
Ok(key.data().as_ffi())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_keymap(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
keymap_id: u64,
|
||||||
|
) -> Result<String> {
|
||||||
|
let keymaps = KEYMAPS.lock();
|
||||||
|
let Some(keymap) = keymaps.get(KeyData::from_ffi(keymap_id).into()) else {
|
||||||
|
bail!("Could not find keymap. Try registering it");
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(keymap.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
177
src/nodes/mod.rs
177
src/nodes/mod.rs
@@ -1,6 +1,5 @@
|
|||||||
pub mod alias;
|
pub mod alias;
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
pub mod data;
|
|
||||||
pub mod drawable;
|
pub mod drawable;
|
||||||
pub mod fields;
|
pub mod fields;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
@@ -10,13 +9,11 @@ pub mod spatial;
|
|||||||
|
|
||||||
use self::alias::Alias;
|
use self::alias::Alias;
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
|
use crate::core::error::{Result, ServerError};
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
use crate::core::scenegraph::MethodResponseSender;
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use dashmap::DashMap;
|
||||||
use parking_lot::Mutex;
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use portable_atomic::{AtomicBool, Ordering};
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
use spatial::Spatial;
|
use spatial::Spatial;
|
||||||
use stardust_xr::messenger::MessageSenderHandle;
|
use stardust_xr::messenger::MessageSenderHandle;
|
||||||
use stardust_xr::scenegraph::ScenegraphError;
|
use stardust_xr::scenegraph::ScenegraphError;
|
||||||
@@ -24,6 +21,7 @@ use stardust_xr::schemas::flex::{deserialize, serialize};
|
|||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
@@ -46,11 +44,29 @@ impl AsRef<[u8]> for Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Signal = fn(Arc<Node>, Arc<Client>, Message) -> Result<()>;
|
|
||||||
pub type Method = fn(Arc<Node>, Arc<Client>, Message, MethodResponseSender);
|
|
||||||
|
|
||||||
stardust_xr_server_codegen::codegen_node_protocol!();
|
stardust_xr_server_codegen::codegen_node_protocol!();
|
||||||
|
|
||||||
|
pub struct Owned;
|
||||||
|
impl AspectIdentifier for Owned {
|
||||||
|
impl_aspect_for_owned_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for Owned {
|
||||||
|
impl_aspect_for_owned_aspect! {}
|
||||||
|
}
|
||||||
|
impl OwnedAspect for Owned {
|
||||||
|
fn set_enabled(node: Arc<Node>, _calling_client: Arc<Client>, enabled: bool) -> Result<()> {
|
||||||
|
node.set_enabled(enabled);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
|
if node.destroyable {
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct OwnedNode(pub Arc<Node>);
|
pub struct OwnedNode(pub Arc<Node>);
|
||||||
impl Drop for OwnedNode {
|
impl Drop for OwnedNode {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -64,8 +80,6 @@ pub struct Node {
|
|||||||
client: Weak<Client>,
|
client: Weak<Client>,
|
||||||
message_sender_handle: Option<MessageSenderHandle>,
|
message_sender_handle: Option<MessageSenderHandle>,
|
||||||
|
|
||||||
local_signals: Mutex<FxHashMap<u64, Signal>>,
|
|
||||||
local_methods: Mutex<FxHashMap<u64, Method>>,
|
|
||||||
aliases: Registry<Alias>,
|
aliases: Registry<Alias>,
|
||||||
aspects: Aspects,
|
aspects: Aspects,
|
||||||
destroyable: bool,
|
destroyable: bool,
|
||||||
@@ -87,26 +101,24 @@ impl Node {
|
|||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
message_sender_handle: client.message_sender_handle.clone(),
|
message_sender_handle: client.message_sender_handle.clone(),
|
||||||
id,
|
id,
|
||||||
local_signals: Default::default(),
|
|
||||||
local_methods: Default::default(),
|
|
||||||
aliases: Default::default(),
|
aliases: Default::default(),
|
||||||
aspects: Default::default(),
|
aspects: Default::default(),
|
||||||
destroyable,
|
destroyable,
|
||||||
};
|
};
|
||||||
<Node as OwnedAspect>::add_node_members(&node);
|
node.aspects.add(Owned);
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
pub fn add_to_scenegraph(self) -> Result<Arc<Node>> {
|
pub fn add_to_scenegraph(self) -> Result<Arc<Node>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_client()
|
.get_client()
|
||||||
.ok_or_else(|| eyre!("Internal: Unable to get client"))?
|
.ok_or(ServerError::NoClient)?
|
||||||
.scenegraph
|
.scenegraph
|
||||||
.add_node(self))
|
.add_node(self))
|
||||||
}
|
}
|
||||||
pub fn add_to_scenegraph_owned(self) -> Result<OwnedNode> {
|
pub fn add_to_scenegraph_owned(self) -> Result<OwnedNode> {
|
||||||
Ok(OwnedNode(
|
Ok(OwnedNode(
|
||||||
self.get_client()
|
self.get_client()
|
||||||
.ok_or_else(|| eyre!("Internal: Unable to get client"))?
|
.ok_or(ServerError::NoClient)?
|
||||||
.scenegraph
|
.scenegraph
|
||||||
.add_node(self),
|
.add_node(self),
|
||||||
))
|
))
|
||||||
@@ -118,7 +130,8 @@ impl Node {
|
|||||||
.global_transform()
|
.global_transform()
|
||||||
.to_scale_rotation_translation()
|
.to_scale_rotation_translation()
|
||||||
.0
|
.0
|
||||||
.length_squared() > 0.0
|
.length_squared()
|
||||||
|
> 0.0
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -146,60 +159,57 @@ impl Node {
|
|||||||
// Ok(serialize(pid)?.into())
|
// Ok(serialize(pid)?.into())
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn add_local_signal(&self, id: u64, signal: Signal) {
|
pub fn add_aspect<A: AspectIdentifier>(&self, aspect: A) -> Arc<A> {
|
||||||
self.local_signals.lock().insert(id, signal);
|
|
||||||
}
|
|
||||||
pub fn add_local_method(&self, id: u64, method: Method) {
|
|
||||||
self.local_methods.lock().insert(id, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_aspect<A: Aspect>(&self, aspect: A) -> Arc<A> {
|
|
||||||
self.aspects.add(aspect)
|
self.aspects.add(aspect)
|
||||||
}
|
}
|
||||||
pub fn add_aspect_raw<A: Aspect>(&self, aspect: Arc<A>) {
|
pub fn add_aspect_raw<A: AspectIdentifier>(&self, aspect: Arc<A>) {
|
||||||
self.aspects.add_raw(aspect)
|
self.aspects.add_raw(aspect)
|
||||||
}
|
}
|
||||||
pub fn get_aspect<A: Aspect>(&self) -> Result<Arc<A>> {
|
pub fn get_aspect<A: AspectIdentifier>(&self) -> Result<Arc<A>> {
|
||||||
self.aspects.get()
|
self.aspects.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_local_signal(
|
pub fn send_local_signal(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
|
aspect_id: u64,
|
||||||
method: u64,
|
method: u64,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<(), ScenegraphError> {
|
) -> Result<(), ScenegraphError> {
|
||||||
if let Ok(alias) = self.get_aspect::<Alias>() {
|
if let Ok(alias) = self.get_aspect::<Alias>() {
|
||||||
if !alias.info.server_signals.iter().any(|e| *e == method) {
|
if !alias.info.server_signals.iter().any(|e| *e == method) {
|
||||||
return Err(ScenegraphError::SignalNotFound);
|
return Err(ScenegraphError::MemberNotFound);
|
||||||
}
|
}
|
||||||
alias
|
alias
|
||||||
.original
|
.original
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or(ScenegraphError::BrokenAlias)?
|
.ok_or(ScenegraphError::BrokenAlias)?
|
||||||
.send_local_signal(calling_client, method, message)
|
.send_local_signal(calling_client, aspect_id, method, message)
|
||||||
} else {
|
} else {
|
||||||
let signal = self
|
let aspect = self
|
||||||
.local_signals
|
.aspects
|
||||||
.lock()
|
.0
|
||||||
.get(&method)
|
.get(&aspect_id)
|
||||||
.cloned()
|
.ok_or(ScenegraphError::AspectNotFound)?
|
||||||
.ok_or(ScenegraphError::SignalNotFound)?;
|
.clone();
|
||||||
signal(self, calling_client, message).map_err(|error| ScenegraphError::SignalError {
|
aspect
|
||||||
error: error.to_string(),
|
.run_signal(calling_client, self.clone(), method, message)
|
||||||
})
|
.map_err(|error| ScenegraphError::MemberError {
|
||||||
|
error: error.to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn execute_local_method(
|
pub fn execute_local_method(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
|
aspect_id: u64,
|
||||||
method: u64,
|
method: u64,
|
||||||
message: Message,
|
message: Message,
|
||||||
response: MethodResponseSender,
|
response: MethodResponseSender,
|
||||||
) {
|
) {
|
||||||
if let Ok(alias) = self.get_aspect::<Alias>() {
|
if let Ok(alias) = self.get_aspect::<Alias>() {
|
||||||
if !alias.info.server_methods.iter().any(|e| *e == method) {
|
if !alias.info.server_methods.iter().any(|e| *e == method) {
|
||||||
response.send(Err(ScenegraphError::MethodNotFound));
|
response.send(Err(ScenegraphError::MemberNotFound));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(alias) = alias.original.upgrade() else {
|
let Some(alias) = alias.original.upgrade() else {
|
||||||
@@ -208,6 +218,7 @@ impl Node {
|
|||||||
};
|
};
|
||||||
alias.execute_local_method(
|
alias.execute_local_method(
|
||||||
calling_client,
|
calling_client,
|
||||||
|
aspect_id,
|
||||||
method,
|
method,
|
||||||
Message {
|
Message {
|
||||||
data: message.data.clone(),
|
data: message.data.clone(),
|
||||||
@@ -216,14 +227,19 @@ impl Node {
|
|||||||
response,
|
response,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let Some(method) = self.local_methods.lock().get(&method).cloned() else {
|
let Some(aspect) = self.aspects.0.get(&aspect_id).map(|v| v.clone()) else {
|
||||||
response.send(Err(ScenegraphError::MethodNotFound));
|
response.send(Err(ScenegraphError::AspectNotFound));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
method(self, calling_client, message, response);
|
aspect.run_method(calling_client, self.clone(), method, message, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn send_remote_signal(&self, method: u64, message: impl Into<Message>) -> Result<()> {
|
pub fn send_remote_signal(
|
||||||
|
&self,
|
||||||
|
aspect_id: u64,
|
||||||
|
method: u64,
|
||||||
|
message: impl Into<Message>,
|
||||||
|
) -> Result<()> {
|
||||||
let message = message.into();
|
let message = message.into();
|
||||||
self.aliases
|
self.aliases
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
@@ -233,6 +249,7 @@ impl Node {
|
|||||||
.for_each(|node| {
|
.for_each(|node| {
|
||||||
// Beware! file descriptors will not be sent to aliases!!!
|
// Beware! file descriptors will not be sent to aliases!!!
|
||||||
let _ = node.send_remote_signal(
|
let _ = node.send_remote_signal(
|
||||||
|
aspect_id,
|
||||||
method,
|
method,
|
||||||
Message {
|
Message {
|
||||||
data: message.data.clone(),
|
data: message.data.clone(),
|
||||||
@@ -241,12 +258,13 @@ impl Node {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
if let Some(handle) = self.message_sender_handle.as_ref() {
|
if let Some(handle) = self.message_sender_handle.as_ref() {
|
||||||
handle.signal(self.id, method, &message.data, message.fds)?;
|
handle.signal(self.id, aspect_id, method, &message.data, message.fds)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub async fn execute_remote_method_typed<S: Serialize, D: DeserializeOwned>(
|
pub async fn execute_remote_method_typed<S: Serialize, D: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
|
aspect_id: u64,
|
||||||
method: u64,
|
method: u64,
|
||||||
input: S,
|
input: S,
|
||||||
fds: Vec<OwnedFd>,
|
fds: Vec<OwnedFd>,
|
||||||
@@ -254,13 +272,13 @@ impl Node {
|
|||||||
let message_sender_handle = self
|
let message_sender_handle = self
|
||||||
.message_sender_handle
|
.message_sender_handle
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(eyre!("Messenger does not exist for this node"))?;
|
.ok_or(ServerError::NoMessenger)?;
|
||||||
|
|
||||||
let serialized = serialize(input)?;
|
let serialized = serialize(input)?;
|
||||||
let result = message_sender_handle
|
let result = message_sender_handle
|
||||||
.method(self.id, method, &serialized, fds)?
|
.method(self.id, aspect_id, method, &serialized, fds)
|
||||||
.await
|
.await?
|
||||||
.map_err(|e| eyre!(e))?;
|
.map_err(ServerError::RemoteMethodError)?;
|
||||||
|
|
||||||
let (message, fds) = result.into_components();
|
let (message, fds) = result.into_components();
|
||||||
let deserialized: D = deserialize(&message)?;
|
let deserialized: D = deserialize(&message)?;
|
||||||
@@ -271,61 +289,62 @@ impl Debug for Node {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Node")
|
f.debug_struct("Node")
|
||||||
.field("id", &self.id)
|
.field("id", &self.id)
|
||||||
.field("local_signals", &self.local_signals.lock().keys())
|
|
||||||
.field("local_methods", &self.local_methods.lock().keys())
|
|
||||||
.field("destroyable", &self.destroyable)
|
.field("destroyable", &self.destroyable)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl OwnedAspect for Node {
|
|
||||||
fn set_enabled(node: Arc<Node>, _calling_client: Arc<Client>, enabled: bool) -> Result<()> {
|
|
||||||
node.set_enabled(enabled);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn destroy(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
|
||||||
if node.destroyable {
|
|
||||||
node.destroy();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for Node {
|
impl Drop for Node {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Debug breakpoint
|
// Debug breakpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AspectIdentifier: Aspect {
|
||||||
|
const ID: u64;
|
||||||
|
}
|
||||||
pub trait Aspect: Any + Send + Sync + 'static {
|
pub trait Aspect: Any + Send + Sync + 'static {
|
||||||
const NAME: &'static str;
|
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync + 'static>;
|
||||||
|
fn run_signal(
|
||||||
|
&self,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
node: Arc<Node>,
|
||||||
|
signal: u64,
|
||||||
|
message: Message,
|
||||||
|
) -> Result<(), stardust_xr::scenegraph::ScenegraphError>;
|
||||||
|
fn run_method(
|
||||||
|
&self,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
node: Arc<Node>,
|
||||||
|
method: u64,
|
||||||
|
message: Message,
|
||||||
|
response: MethodResponseSender,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Aspects(Mutex<FxHashMap<TypeId, Arc<dyn Any + Send + Sync + 'static>>>);
|
struct Aspects(DashMap<u64, Arc<dyn Aspect>>);
|
||||||
|
|
||||||
impl Aspects {
|
impl Aspects {
|
||||||
fn add<A: Aspect>(&self, t: A) -> Arc<A> {
|
fn add<A: AspectIdentifier>(&self, t: A) -> Arc<A> {
|
||||||
let aspect = Arc::new(t);
|
let aspect = Arc::new(t);
|
||||||
self.add_raw(aspect.clone());
|
self.add_raw(aspect.clone());
|
||||||
aspect
|
aspect
|
||||||
}
|
}
|
||||||
fn add_raw<A: Aspect>(&self, aspect: Arc<A>) {
|
fn add_raw<A: AspectIdentifier>(&self, aspect: Arc<A>) {
|
||||||
self.0.lock().insert(Self::type_key::<A>(), aspect);
|
self.0.insert(A::ID, aspect);
|
||||||
}
|
}
|
||||||
fn get<A: Aspect>(&self) -> Result<Arc<A>> {
|
fn get<A: Aspect + AspectIdentifier>(&self) -> Result<Arc<A>> {
|
||||||
self.0
|
self.0
|
||||||
.lock()
|
.get(&A::ID)
|
||||||
.get(&Self::type_key::<A>())
|
// .cloned doesn't work for some reason
|
||||||
.and_then(|a| Arc::downcast(a.clone()).ok())
|
.map(|v| v.clone())
|
||||||
.ok_or(eyre!("Couldn't get aspect {}", A::NAME.to_lowercase()))
|
.map(|a| a.as_any())
|
||||||
}
|
.and_then(|a| Arc::downcast(a).ok())
|
||||||
|
.ok_or(ServerError::NoAspect(TypeId::of::<A>()))
|
||||||
fn type_key<A: 'static>() -> TypeId {
|
|
||||||
TypeId::of::<A>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Aspects {
|
impl Drop for Aspects {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.0.lock().clear()
|
// why would this be needed? do drop impls not run otherwise?
|
||||||
|
self.0.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
use super::spatial::Spatial;
|
use super::spatial::Spatial;
|
||||||
use super::Node;
|
use super::{Aspect, AspectIdentifier, Node};
|
||||||
use crate::core::client::Client;
|
use crate::bail;
|
||||||
|
use crate::core::client::{CLIENTS, Client};
|
||||||
use crate::core::client_state::ClientStateParsed;
|
use crate::core::client_state::ClientStateParsed;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::error::Result;
|
||||||
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
||||||
use crate::session::connection_env;
|
use crate::session::connection_env;
|
||||||
use color_eyre::eyre::{bail, Result};
|
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
|
||||||
|
|
||||||
stardust_xr_server_codegen::codegen_root_protocol!();
|
stardust_xr_server_codegen::codegen_root_protocol!();
|
||||||
|
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
@@ -23,18 +21,20 @@ pub struct Root {
|
|||||||
impl Root {
|
impl Root {
|
||||||
pub fn create(client: &Arc<Client>, transform: Mat4) -> Result<Arc<Self>> {
|
pub fn create(client: &Arc<Client>, transform: Mat4) -> Result<Arc<Self>> {
|
||||||
let node = Node::from_id(client, 0, false);
|
let node = Node::from_id(client, 0, false);
|
||||||
<Self as RootAspect>::add_node_members(&node);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
let node = node.add_to_scenegraph()?;
|
||||||
let _ = Spatial::add_to(&node, None, transform, false);
|
let _ = Spatial::add_to(&node, None, transform, false);
|
||||||
|
let root_aspect = node.add_aspect(Root {
|
||||||
Ok(ROOT_REGISTRY.add(Root {
|
node: node.clone(),
|
||||||
node,
|
|
||||||
connect_instant: Instant::now(),
|
connect_instant: Instant::now(),
|
||||||
}))
|
});
|
||||||
|
Ok(root_aspect)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_frame_events(delta: f64) {
|
pub fn send_frame_events(delta: f64) {
|
||||||
for root in ROOT_REGISTRY.get_valid_contents() {
|
for client in CLIENTS.get_vec() {
|
||||||
|
let Some(root) = client.root.get() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let _ = root_client::frame(
|
let _ = root_client::frame(
|
||||||
&root.node,
|
&root.node,
|
||||||
&FrameInfo {
|
&FrameInfo {
|
||||||
@@ -47,13 +47,19 @@ impl Root {
|
|||||||
|
|
||||||
pub fn set_transform(&self, transform: Mat4) {
|
pub fn set_transform(&self, transform: Mat4) {
|
||||||
let spatial = self.node.get_aspect::<Spatial>().unwrap();
|
let spatial = self.node.get_aspect::<Spatial>().unwrap();
|
||||||
spatial.set_spatial_parent(None).unwrap();
|
// spatial.set_spatial_parent(None).unwrap();
|
||||||
spatial.set_local_transform(transform);
|
spatial.set_local_transform(transform);
|
||||||
}
|
}
|
||||||
pub async fn save_state(&self) -> Result<ClientState> {
|
pub async fn save_state(&self) -> Result<ClientState> {
|
||||||
Ok(root_client::save_state(&self.node).await?.0)
|
Ok(root_client::save_state(&self.node).await?.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for Root {
|
||||||
|
impl_aspect_for_root_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for Root {
|
||||||
|
impl_aspect_for_root_aspect! {}
|
||||||
|
}
|
||||||
impl RootAspect for Root {
|
impl RootAspect for Root {
|
||||||
async fn get_state(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<ClientState> {
|
async fn get_state(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<ClientState> {
|
||||||
let Some(state) = calling_client.state.get() else {
|
let Some(state) = calling_client.state.get() else {
|
||||||
@@ -92,13 +98,8 @@ impl RootAspect for Root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "Cleanly disconnect from the server"]
|
#[doc = "Cleanly disconnect from the server"]
|
||||||
fn disconnect(_node: Arc<Node>, calling_client: Arc<Client>) -> color_eyre::eyre::Result<()> {
|
fn disconnect(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
|
||||||
calling_client.disconnect(Ok(()));
|
calling_client.disconnect(Ok(()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Root {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
ROOT_REGISTRY.remove(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ pub mod zone;
|
|||||||
use self::zone::Zone;
|
use self::zone::Zone;
|
||||||
use super::alias::Alias;
|
use super::alias::Alias;
|
||||||
use super::fields::{Field, FieldTrait};
|
use super::fields::{Field, FieldTrait};
|
||||||
use super::Aspect;
|
use super::{Aspect, AspectIdentifier};
|
||||||
|
use crate::bail;
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
|
use crate::core::error::Result;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::create_interface;
|
|
||||||
use crate::nodes::{Node, OWNED_ASPECT_ALIAS_INFO};
|
use crate::nodes::{Node, OWNED_ASPECT_ALIAS_INFO};
|
||||||
use color_eyre::eyre::{eyre, OptionExt, Result};
|
use color_eyre::eyre::OptionExt;
|
||||||
use glam::{vec3a, Mat4, Quat, Vec3};
|
use glam::{Mat4, Quat, Vec3, vec3a};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ptr;
|
use std::sync::{Arc, OnceLock, Weak};
|
||||||
use std::sync::{Arc, Weak};
|
use std::{f32, ptr};
|
||||||
use stereokit_rust::maths::Bounds;
|
use stereokit_rust::maths::Bounds;
|
||||||
|
|
||||||
stardust_xr_server_codegen::codegen_spatial_protocol!();
|
stardust_xr_server_codegen::codegen_spatial_protocol!();
|
||||||
@@ -38,6 +38,12 @@ impl Transform {
|
|||||||
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AspectIdentifier for Zone {
|
||||||
|
impl_aspect_for_zone_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for Zone {
|
||||||
|
impl_aspect_for_zone_aspect! {}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref EXPORTED_SPATIALS: Mutex<FxHashMap<u64, Arc<Node>>> = Mutex::new(FxHashMap::default());
|
pub static ref EXPORTED_SPATIALS: Mutex<FxHashMap<u64, Arc<Node>>> = Mutex::new(FxHashMap::default());
|
||||||
@@ -52,7 +58,7 @@ pub struct Spatial {
|
|||||||
transform: Mutex<Mat4>,
|
transform: Mutex<Mat4>,
|
||||||
zone: Mutex<Weak<Zone>>,
|
zone: Mutex<Weak<Zone>>,
|
||||||
children: Registry<Spatial>,
|
children: Registry<Spatial>,
|
||||||
pub bounding_box_calc: OnceCell<fn(&Node) -> Bounds>,
|
pub bounding_box_calc: OnceLock<fn(&Node) -> Bounds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spatial {
|
impl Spatial {
|
||||||
@@ -64,7 +70,7 @@ impl Spatial {
|
|||||||
transform: Mutex::new(transform),
|
transform: Mutex::new(transform),
|
||||||
zone: Mutex::new(Weak::new()),
|
zone: Mutex::new(Weak::new()),
|
||||||
children: Registry::new(),
|
children: Registry::new(),
|
||||||
bounding_box_calc: OnceCell::default(),
|
bounding_box_calc: OnceLock::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
@@ -74,16 +80,14 @@ impl Spatial {
|
|||||||
zoneable: bool,
|
zoneable: bool,
|
||||||
) -> Arc<Spatial> {
|
) -> Arc<Spatial> {
|
||||||
let spatial = Spatial::new(Arc::downgrade(node), parent.clone(), transform);
|
let spatial = Spatial::new(Arc::downgrade(node), parent.clone(), transform);
|
||||||
<Spatial as SpatialAspect>::add_node_members(node);
|
|
||||||
if zoneable {
|
if zoneable {
|
||||||
ZONEABLE_REGISTRY.add_raw(&spatial);
|
ZONEABLE_REGISTRY.add_raw(&spatial);
|
||||||
}
|
}
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
parent.children.add_raw(&spatial);
|
parent.children.add_raw(&spatial);
|
||||||
}
|
}
|
||||||
<Spatial as SpatialRefAspect>::add_node_members(node);
|
|
||||||
<Spatial as SpatialAspect>::add_node_members(node);
|
|
||||||
node.add_aspect_raw(spatial.clone());
|
node.add_aspect_raw(spatial.clone());
|
||||||
|
node.add_aspect(SpatialRef);
|
||||||
spatial
|
spatial
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,45 +192,29 @@ impl Spatial {
|
|||||||
fn get_parent(&self) -> Option<Arc<Spatial>> {
|
fn get_parent(&self) -> Option<Arc<Spatial>> {
|
||||||
self.parent.lock().clone()
|
self.parent.lock().clone()
|
||||||
}
|
}
|
||||||
fn set_parent(self: &Arc<Self>, new_parent: Option<&Arc<Spatial>>) {
|
fn set_parent(self: &Arc<Self>, new_parent: &Arc<Spatial>) {
|
||||||
if let Some(parent) = self.get_parent() {
|
if let Some(parent) = self.get_parent() {
|
||||||
parent.children.remove(self);
|
parent.children.remove(self);
|
||||||
}
|
}
|
||||||
if let Some(new_parent) = &new_parent {
|
new_parent.children.add_raw(self);
|
||||||
new_parent.children.add_raw(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
*self.parent.lock() = new_parent.cloned();
|
*self.parent.lock() = Some(new_parent.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_spatial_parent(self: &Arc<Self>, parent: Option<&Arc<Spatial>>) -> Result<()> {
|
pub fn set_spatial_parent(self: &Arc<Self>, parent: &Arc<Spatial>) -> Result<()> {
|
||||||
let is_ancestor = parent
|
if self.is_ancestor_of(parent.clone()) {
|
||||||
.as_ref()
|
bail!("Setting spatial parent would cause a loop");
|
||||||
.map(|parent| self.is_ancestor_of((*parent).clone()))
|
|
||||||
.unwrap_or(false);
|
|
||||||
if is_ancestor {
|
|
||||||
return Err(eyre!("Setting spatial parent would cause a loop"));
|
|
||||||
}
|
}
|
||||||
self.set_parent(parent);
|
self.set_parent(parent);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn set_spatial_parent_in_place(
|
pub fn set_spatial_parent_in_place(self: &Arc<Self>, parent: &Arc<Spatial>) -> Result<()> {
|
||||||
self: &Arc<Self>,
|
if self.is_ancestor_of(parent.clone()) {
|
||||||
parent: Option<&Arc<Spatial>>,
|
bail!("Setting spatial parent would cause a loop");
|
||||||
) -> Result<()> {
|
|
||||||
let is_ancestor = parent
|
|
||||||
.as_ref()
|
|
||||||
.map(|parent| self.is_ancestor_of((*parent).clone()))
|
|
||||||
.unwrap_or(false);
|
|
||||||
if is_ancestor {
|
|
||||||
return Err(eyre!("Setting spatial parent would cause a loop"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_local_transform(Spatial::space_to_space_matrix(
|
self.set_local_transform(Spatial::space_to_space_matrix(Some(self), Some(parent)));
|
||||||
Some(self),
|
|
||||||
parent.map(AsRef::as_ref),
|
|
||||||
));
|
|
||||||
self.set_parent(parent);
|
self.set_parent(parent);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -238,13 +226,109 @@ impl Spatial {
|
|||||||
.upgrade()
|
.upgrade()
|
||||||
.map(|zone| zone.field.clone())
|
.map(|zone| zone.field.clone())
|
||||||
.map(|field| field.distance(self, vec3a(0.0, 0.0, 0.0)))
|
.map(|field| field.distance(self, vec3a(0.0, 0.0, 0.0)))
|
||||||
.unwrap_or(f32::MAX)
|
.unwrap_or(f32::NEG_INFINITY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for Spatial {
|
impl AspectIdentifier for Spatial {
|
||||||
const NAME: &'static str = "Spatial";
|
impl_aspect_for_spatial_aspect_id! {}
|
||||||
}
|
}
|
||||||
impl SpatialRefAspect for Spatial {
|
impl Aspect for Spatial {
|
||||||
|
impl_aspect_for_spatial_aspect! {}
|
||||||
|
}
|
||||||
|
impl SpatialAspect for Spatial {
|
||||||
|
fn set_local_transform(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
transform: Transform,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
this_spatial.set_local_transform_components(None, transform);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn set_relative_transform(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
relative_to: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
||||||
|
|
||||||
|
this_spatial.set_local_transform_components(Some(&relative_spatial), transform);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_spatial_parent(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
|
||||||
|
this_spatial.set_spatial_parent(&parent)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_spatial_parent_in_place(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
|
||||||
|
this_spatial.set_spatial_parent_in_place(&parent)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_zoneable(node: Arc<Node>, _calling_client: Arc<Client>, zoneable: bool) -> Result<()> {
|
||||||
|
let spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
if zoneable {
|
||||||
|
ZONEABLE_REGISTRY.add_raw(&spatial);
|
||||||
|
} else {
|
||||||
|
ZONEABLE_REGISTRY.remove(&spatial);
|
||||||
|
zone::release(&spatial);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// legit gotta find a way to remove old ones, this just keeps the node alive
|
||||||
|
async fn export_spatial(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<u64> {
|
||||||
|
let id = rand::random();
|
||||||
|
EXPORTED_SPATIALS.lock().insert(id, node);
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialEq for Spatial {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.node.as_ptr() == other.node.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Debug for Spatial {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Spatial")
|
||||||
|
.field("parent", &self.parent)
|
||||||
|
.field("old_parent", &self.old_parent)
|
||||||
|
.field("transform", &self.transform)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for Spatial {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
zone::release(self);
|
||||||
|
ZONEABLE_REGISTRY.remove(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SpatialRef;
|
||||||
|
impl AspectIdentifier for SpatialRef {
|
||||||
|
impl_aspect_for_spatial_ref_aspect_id! {}
|
||||||
|
}
|
||||||
|
impl Aspect for SpatialRef {
|
||||||
|
impl_aspect_for_spatial_ref_aspect! {}
|
||||||
|
}
|
||||||
|
impl SpatialRefAspect for SpatialRef {
|
||||||
async fn get_local_bounding_box(
|
async fn get_local_bounding_box(
|
||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
@@ -303,91 +387,6 @@ impl SpatialRefAspect for Spatial {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SpatialAspect for Spatial {
|
|
||||||
fn set_local_transform(
|
|
||||||
node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
transform: Transform,
|
|
||||||
) -> Result<()> {
|
|
||||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
|
||||||
this_spatial.set_local_transform_components(None, transform);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn set_relative_transform(
|
|
||||||
node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
relative_to: Arc<Node>,
|
|
||||||
transform: Transform,
|
|
||||||
) -> Result<()> {
|
|
||||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
|
||||||
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
|
||||||
|
|
||||||
this_spatial.set_local_transform_components(Some(&relative_spatial), transform);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_spatial_parent(
|
|
||||||
node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
parent: Arc<Node>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
|
||||||
let parent = parent.get_aspect::<Spatial>()?;
|
|
||||||
|
|
||||||
this_spatial.set_spatial_parent(Some(&parent))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_spatial_parent_in_place(
|
|
||||||
node: Arc<Node>,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
parent: Arc<Node>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
|
||||||
let parent = parent.get_aspect::<Spatial>()?;
|
|
||||||
|
|
||||||
this_spatial.set_spatial_parent_in_place(Some(&parent))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_zoneable(node: Arc<Node>, _calling_client: Arc<Client>, zoneable: bool) -> Result<()> {
|
|
||||||
let spatial = node.get_aspect::<Spatial>()?;
|
|
||||||
if zoneable {
|
|
||||||
ZONEABLE_REGISTRY.add_raw(&spatial);
|
|
||||||
} else {
|
|
||||||
ZONEABLE_REGISTRY.remove(&spatial);
|
|
||||||
zone::release(&spatial);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// legit gotta find a way to remove old ones, this just keeps the node alive
|
|
||||||
async fn export_spatial(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<u64> {
|
|
||||||
let id = rand::random();
|
|
||||||
EXPORTED_SPATIALS.lock().insert(id, node);
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PartialEq for Spatial {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.node.as_ptr() == other.node.as_ptr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Debug for Spatial {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("Spatial")
|
|
||||||
.field("parent", &self.parent)
|
|
||||||
.field("old_parent", &self.old_parent)
|
|
||||||
.field("transform", &self.transform)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for Spatial {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
zone::release(self);
|
|
||||||
ZONEABLE_REGISTRY.remove(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_transform(transform: Transform, position: bool, rotation: bool, scale: bool) -> Mat4 {
|
pub fn parse_transform(transform: Transform, position: bool, rotation: bool, scale: bool) -> Mat4 {
|
||||||
let position = position
|
let position = position
|
||||||
@@ -406,8 +405,7 @@ pub fn parse_transform(transform: Transform, position: bool, rotation: bool, sca
|
|||||||
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpatialInterface;
|
impl InterfaceAspect for Interface {
|
||||||
impl InterfaceAspect for SpatialInterface {
|
|
||||||
fn create_spatial(
|
fn create_spatial(
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
@@ -445,7 +443,7 @@ impl InterfaceAspect for SpatialInterface {
|
|||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
uid: u64,
|
uid: u64,
|
||||||
) -> Result<Arc<Node>> {
|
) -> Result<Arc<Node>> {
|
||||||
EXPORTED_SPATIALS
|
Ok(EXPORTED_SPATIALS
|
||||||
.lock()
|
.lock()
|
||||||
.get(&uid)
|
.get(&uid)
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
@@ -457,8 +455,6 @@ impl InterfaceAspect for SpatialInterface {
|
|||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
.ok_or_eyre("Couldn't find spatial with that ID")
|
.ok_or_eyre("Couldn't find spatial with that ID")?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_interface!(SpatialInterface);
|
|
||||||
|
|||||||
@@ -1,42 +1,43 @@
|
|||||||
use super::{
|
use super::{
|
||||||
Spatial, ZoneAspect, SPATIAL_ASPECT_ALIAS_INFO, SPATIAL_REF_ASPECT_ALIAS_INFO,
|
SPATIAL_ASPECT_ALIAS_INFO, SPATIAL_REF_ASPECT_ALIAS_INFO, Spatial, ZONEABLE_REGISTRY,
|
||||||
ZONEABLE_REGISTRY,
|
ZoneAspect,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, registry::Registry},
|
core::{client::Client, error::Result, registry::Registry},
|
||||||
nodes::{
|
nodes::{
|
||||||
alias::{get_original, Alias, AliasList},
|
Node,
|
||||||
|
alias::{Alias, AliasList, get_original},
|
||||||
fields::{Field, FieldTrait},
|
fields::{Field, FieldTrait},
|
||||||
Aspect, Node,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use glam::vec3a;
|
use glam::vec3a;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
|
pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
|
||||||
let old_distance = spatial.zone_distance();
|
let old_distance = spatial.zone_distance();
|
||||||
let new_distance = zone.field.distance(spatial, vec3a(0.0, 0.0, 0.0));
|
let new_distance = zone.field.distance(spatial, vec3a(0.0, 0.0, 0.0));
|
||||||
if new_distance.abs() < old_distance.abs() {
|
if new_distance.abs() > old_distance.abs() {
|
||||||
release(spatial);
|
return;
|
||||||
*spatial.old_parent.lock() = spatial.get_parent();
|
|
||||||
*spatial.zone.lock() = Arc::downgrade(zone);
|
|
||||||
let Some(zone_node) = zone.spatial.node.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(spatial_node) = spatial.node.upgrade() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Ok(spatial_alias) = Alias::create(
|
|
||||||
&spatial_node,
|
|
||||||
&zone_node.get_client().unwrap(),
|
|
||||||
SPATIAL_ASPECT_ALIAS_INFO.clone(),
|
|
||||||
Some(&zone.captured),
|
|
||||||
) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let _ = super::zone_client::capture(&zone_node, &spatial_alias);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
release(spatial);
|
||||||
|
*spatial.old_parent.lock() = spatial.get_parent();
|
||||||
|
*spatial.zone.lock() = Arc::downgrade(zone);
|
||||||
|
let Some(zone_node) = zone.spatial.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(spatial_node) = spatial.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Ok(spatial_alias) = Alias::create(
|
||||||
|
&spatial_node,
|
||||||
|
&zone_node.get_client().unwrap(),
|
||||||
|
SPATIAL_ASPECT_ALIAS_INFO.clone(),
|
||||||
|
Some(&zone.captured),
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let _ = super::zone_client::capture(&zone_node, &spatial_alias);
|
||||||
}
|
}
|
||||||
pub fn release(spatial: &Spatial) {
|
pub fn release(spatial: &Spatial) {
|
||||||
let Some(spatial_node) = spatial.node.upgrade() else {
|
let Some(spatial_node) = spatial.node.upgrade() else {
|
||||||
@@ -44,7 +45,10 @@ pub fn release(spatial: &Spatial) {
|
|||||||
};
|
};
|
||||||
let spatial = spatial_node.get_aspect::<Spatial>().unwrap();
|
let spatial = spatial_node.get_aspect::<Spatial>().unwrap();
|
||||||
|
|
||||||
let _ = spatial.set_spatial_parent_in_place(spatial.old_parent.lock().take().as_ref());
|
let Some(old_parent) = spatial.old_parent.lock().take() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let _ = spatial.set_spatial_parent_in_place(&old_parent);
|
||||||
let mut spatial_zone = spatial.zone.lock();
|
let mut spatial_zone = spatial.zone.lock();
|
||||||
|
|
||||||
if let Some(spatial_zone) = spatial_zone.upgrade() {
|
if let Some(spatial_zone) = spatial_zone.upgrade() {
|
||||||
@@ -73,7 +77,6 @@ impl Zone {
|
|||||||
intersecting: AliasList::default(),
|
intersecting: AliasList::default(),
|
||||||
captured: AliasList::default(),
|
captured: AliasList::default(),
|
||||||
});
|
});
|
||||||
<Zone as ZoneAspect>::add_node_members(node);
|
|
||||||
node.add_aspect_raw(zone.clone());
|
node.add_aspect_raw(zone.clone());
|
||||||
zone
|
zone
|
||||||
}
|
}
|
||||||
@@ -82,15 +85,17 @@ impl Zone {
|
|||||||
|
|
||||||
let current_zoneables = Registry::new();
|
let current_zoneables = Registry::new();
|
||||||
for zoneable in ZONEABLE_REGISTRY.get_valid_contents() {
|
for zoneable in ZONEABLE_REGISTRY.get_valid_contents() {
|
||||||
|
// Skip if the zoneable is an ancestor of the zone or the zone itself
|
||||||
|
if zoneable.is_ancestor_of(self.spatial.clone()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let distance = self.field.distance(&zoneable, [0.0; 3].into());
|
let distance = self.field.distance(&zoneable, [0.0; 3].into());
|
||||||
if distance > 0.0 {
|
if distance > 0.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(zone) = zoneable.zone.lock().upgrade() {
|
if distance < zoneable.zone_distance() {
|
||||||
let zoneable_distance = zone.field.distance(&zoneable, [0.0; 3].into());
|
continue;
|
||||||
if zoneable_distance < distance {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
current_zoneables.add_raw(&zoneable);
|
current_zoneables.add_raw(&zoneable);
|
||||||
}
|
}
|
||||||
@@ -124,9 +129,6 @@ impl Zone {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for Zone {
|
|
||||||
const NAME: &'static str = "Zone";
|
|
||||||
}
|
|
||||||
impl ZoneAspect for Zone {
|
impl ZoneAspect for Zone {
|
||||||
fn update(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
fn update(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
let zone = node.get_aspect::<Zone>()?;
|
let zone = node.get_aspect::<Zone>()?;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
fields::{FieldTrait, Ray},
|
|
||||||
input::{InputDataType, InputMethod, Pointer, INPUT_HANDLER_REGISTRY},
|
|
||||||
spatial::Spatial,
|
|
||||||
Node, OwnedNode,
|
Node, OwnedNode,
|
||||||
|
fields::{FieldTrait, Ray},
|
||||||
|
input::{INPUT_HANDLER_REGISTRY, InputDataType, InputMethod, Pointer},
|
||||||
|
spatial::Spatial,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec3, Mat4};
|
use glam::{Mat4, vec3};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -5,40 +5,43 @@ pub mod sk_hand;
|
|||||||
|
|
||||||
use crate::nodes::{
|
use crate::nodes::{
|
||||||
fields::{Field, FieldTrait, Ray},
|
fields::{Field, FieldTrait, Ray},
|
||||||
input::{InputDataTrait, InputDataType, InputHandler, InputMethod, INPUT_HANDLER_REGISTRY},
|
input::{INPUT_HANDLER_REGISTRY, InputDataTrait, InputDataType, InputHandler, InputMethod},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
};
|
};
|
||||||
use glam::vec3;
|
use glam::vec3;
|
||||||
use std::sync::Arc;
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CaptureManager {
|
pub struct CaptureManager {
|
||||||
pub capture: Option<Arc<InputHandler>>,
|
pub capture: Weak<InputHandler>,
|
||||||
}
|
}
|
||||||
impl CaptureManager {
|
impl CaptureManager {
|
||||||
pub fn update_capture(&mut self, pointer: &InputMethod) {
|
pub fn update_capture(&mut self, method: &InputMethod) {
|
||||||
if let Some(capture) = &self.capture {
|
if let Some(capture) = &self.capture.upgrade() {
|
||||||
if !pointer
|
if !method
|
||||||
.internal_capture_requests
|
.capture_attempts
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
.contains(capture)
|
.contains(capture)
|
||||||
{
|
{
|
||||||
self.capture.take();
|
self.capture = Weak::new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_new_capture(
|
pub fn set_new_capture(
|
||||||
&mut self,
|
&mut self,
|
||||||
pointer: &InputMethod,
|
method: &InputMethod,
|
||||||
distance_calculator: DistanceCalculator,
|
distance_calculator: DistanceCalculator,
|
||||||
) {
|
) {
|
||||||
if self.capture.is_none() {
|
if self.capture.upgrade().is_none() {
|
||||||
self.capture = find_closest_capture(pointer, distance_calculator);
|
self.capture = find_closest_capture(method, distance_calculator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn apply_capture(&self, method: &InputMethod) {
|
pub fn apply_capture(&self, method: &InputMethod) {
|
||||||
method.captures.clear();
|
method.captures.clear();
|
||||||
if let Some(capture) = &self.capture {
|
if let Some(capture) = &self.capture.upgrade() {
|
||||||
method.set_handler_order([capture].into_iter());
|
method.set_handler_order([capture].into_iter());
|
||||||
method.captures.add_raw(capture);
|
method.captures.add_raw(capture);
|
||||||
}
|
}
|
||||||
@@ -50,9 +53,9 @@ type DistanceCalculator = fn(&Arc<Spatial>, &InputDataType, &Field) -> Option<f3
|
|||||||
pub fn find_closest_capture(
|
pub fn find_closest_capture(
|
||||||
method: &InputMethod,
|
method: &InputMethod,
|
||||||
distance_calculator: DistanceCalculator,
|
distance_calculator: DistanceCalculator,
|
||||||
) -> Option<Arc<InputHandler>> {
|
) -> Weak<InputHandler> {
|
||||||
method
|
method
|
||||||
.internal_capture_requests
|
.capture_attempts
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|h| {
|
.filter_map(|h| {
|
||||||
@@ -60,39 +63,31 @@ pub fn find_closest_capture(
|
|||||||
.map(|dist| (h.clone(), dist))
|
.map(|dist| (h.clone(), dist))
|
||||||
})
|
})
|
||||||
.min_by(|(_, dist_a), (_, dist_b)| dist_a.partial_cmp(dist_b).unwrap())
|
.min_by(|(_, dist_a), (_, dist_b)| dist_a.partial_cmp(dist_b).unwrap())
|
||||||
.map(|(handler, _)| handler)
|
.map(|(handler, _)| Arc::downgrade(&handler))
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// sorts them greatest to least distance (so you can pop off the closest ones easily)
|
||||||
pub fn get_sorted_handlers(
|
pub fn get_sorted_handlers(
|
||||||
method: &InputMethod,
|
method: &InputMethod,
|
||||||
distance_calculator: DistanceCalculator,
|
distance_calculator: DistanceCalculator,
|
||||||
) -> Vec<Arc<InputHandler>> {
|
) -> Vec<(Arc<InputHandler>, f32)> {
|
||||||
INPUT_HANDLER_REGISTRY
|
let mut handlers = INPUT_HANDLER_REGISTRY
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|handler| handler.spatial.node().map_or(false, |node| node.enabled()))
|
.filter(|handler| handler.spatial.node().is_some_and(|node| node.enabled()))
|
||||||
.filter(|handler| {
|
.filter(|handler| {
|
||||||
handler
|
handler
|
||||||
.field
|
.field
|
||||||
.spatial
|
.spatial
|
||||||
.node()
|
.node()
|
||||||
.map_or(false, |node| node.enabled())
|
.is_some_and(|node| node.enabled())
|
||||||
})
|
})
|
||||||
.filter_map(|handler| {
|
.filter_map(|handler| {
|
||||||
distance_calculator(&method.spatial, &method.data.lock(), &handler.field)
|
distance_calculator(&method.spatial, &method.data.lock(), &handler.field)
|
||||||
.map(|distance| (vec![handler], distance))
|
.map(|distance| (handler, distance))
|
||||||
})
|
})
|
||||||
.filter(|(_, distance)| *distance > 0.0)
|
.collect::<Vec<_>>();
|
||||||
.reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| {
|
handlers.sort_by(|(_, dist_a), (_, dist_b)| dist_a.partial_cmp(dist_b).unwrap());
|
||||||
if (distance_a - distance_b).abs() < 0.001 {
|
handlers
|
||||||
handlers_a.extend(handlers_b);
|
|
||||||
(handlers_a, distance_a)
|
|
||||||
} else if distance_a < distance_b {
|
|
||||||
(handlers_a, distance_a)
|
|
||||||
} else {
|
|
||||||
(handlers_b, distance_b)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|(handlers, _)| handlers)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
|
use super::{CaptureManager, DistanceCalculator, get_sorted_handlers};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
data::{
|
|
||||||
mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY,
|
|
||||||
},
|
|
||||||
fields::{FieldTrait, Ray},
|
|
||||||
input::{InputDataType, InputHandler, InputMethod, Pointer, INPUT_HANDLER_REGISTRY},
|
|
||||||
spatial::Spatial,
|
|
||||||
Node, OwnedNode,
|
Node, OwnedNode,
|
||||||
|
fields::{EXPORTED_FIELDS, Field, FieldTrait, Ray},
|
||||||
|
input::{InputDataType, InputMethod, Pointer},
|
||||||
|
items::panel::KEYMAPS,
|
||||||
|
spatial::Spatial,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec3, Mat4, Vec3};
|
use glam::{Mat4, Vec3, vec3};
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slotmap::{DefaultKey, Key as SlotKey};
|
use slotmap::{DefaultKey, Key as SlotKey};
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::{
|
||||||
|
schemas::dbus::{interfaces::FieldRefProxy, object_registry::ObjectRegistry},
|
||||||
|
values::Datamap,
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit_rust::system::{Input, Key};
|
use stereokit_rust::system::{Input, Key};
|
||||||
use xkbcommon_rs::{xkb_keymap::CompileFlags, Context, Keymap, KeymapFormat};
|
use tokio::task::JoinSet;
|
||||||
|
use tokio::time::{Duration, timeout};
|
||||||
use super::{get_sorted_handlers, CaptureManager, DistanceCalculator};
|
use xkbcommon_rs::{Context, Keymap, KeymapFormat, xkb_keymap::CompileFlags};
|
||||||
|
use zbus::{Connection, names::OwnedInterfaceName};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct MouseEvent {
|
struct MouseEvent {
|
||||||
@@ -46,12 +49,14 @@ impl Default for MouseEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
#[zbus::proxy(
|
||||||
pub struct KeyboardEvent {
|
interface = "org.stardustxr.XKBv1",
|
||||||
pub keyboard: (),
|
default_service = "org.stardustxr.XKBv1"
|
||||||
pub xkbv1: (),
|
)]
|
||||||
pub keymap_id: u64,
|
trait KeyboardHandler {
|
||||||
pub keys: Vec<i32>,
|
async fn keymap(&self, keymap_id: u64) -> zbus::Result<()>;
|
||||||
|
async fn key_state(&self, key: u32, pressed: bool) -> zbus::Result<()>;
|
||||||
|
async fn reset(&self) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -62,8 +67,6 @@ pub struct MousePointer {
|
|||||||
pointer: Arc<InputMethod>,
|
pointer: Arc<InputMethod>,
|
||||||
capture_manager: CaptureManager,
|
capture_manager: CaptureManager,
|
||||||
mouse_datamap: MouseEvent,
|
mouse_datamap: MouseEvent,
|
||||||
keyboard_datamap: KeyboardEvent,
|
|
||||||
keyboard_sender: Arc<PulseSender>,
|
|
||||||
}
|
}
|
||||||
impl MousePointer {
|
impl MousePointer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
@@ -83,29 +86,16 @@ impl MousePointer {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let keyboard_sender = PulseSender::add_to(
|
|
||||||
&node.0,
|
|
||||||
Datamap::from_typed(KeyboardEvent::default()).unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(MousePointer {
|
Ok(MousePointer {
|
||||||
node,
|
node,
|
||||||
spatial,
|
spatial,
|
||||||
pointer,
|
pointer,
|
||||||
capture_manager: CaptureManager::default(),
|
capture_manager: CaptureManager::default(),
|
||||||
mouse_datamap: Default::default(),
|
mouse_datamap: Default::default(),
|
||||||
keyboard_datamap: KeyboardEvent {
|
|
||||||
keyboard: (),
|
|
||||||
xkbv1: (),
|
|
||||||
keymap_id: keymap.data().as_ffi(),
|
|
||||||
keys: vec![],
|
|
||||||
},
|
|
||||||
keymap,
|
keymap,
|
||||||
keyboard_sender,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self, dbus_connection: &Connection, object_registry: &ObjectRegistry) {
|
||||||
let mouse = Input::get_mouse();
|
let mouse = Input::get_mouse();
|
||||||
|
|
||||||
let ray = mouse.get_ray();
|
let ray = mouse.get_ray();
|
||||||
@@ -123,7 +113,9 @@ impl MousePointer {
|
|||||||
select: Input::key(Key::MouseLeft).is_active() as u32 as f32,
|
select: Input::key(Key::MouseLeft).is_active() as u32 as f32,
|
||||||
middle: Input::key(Key::MouseCenter).is_active() as u32 as f32,
|
middle: Input::key(Key::MouseCenter).is_active() as u32 as f32,
|
||||||
context: Input::key(Key::MouseRight).is_active() as u32 as f32,
|
context: Input::key(Key::MouseRight).is_active() as u32 as f32,
|
||||||
grab: (Input::key(Key::Backtick).is_active() && Input::key(Key::Shift).is_active()) as u32 as f32, // Was Mouse 5
|
grab: (Input::key(Key::MouseRight).is_active()
|
||||||
|
|| (Input::key(Key::Backtick).is_active()
|
||||||
|
&& Input::key(Key::Shift).is_active())) as u32 as f32, // Was Mouse 5
|
||||||
scroll_continuous: [0.0, mouse.scroll_change / 120.0].into(),
|
scroll_continuous: [0.0, mouse.scroll_change / 120.0].into(),
|
||||||
scroll_discrete: [0.0, mouse.scroll_change / 120.0].into(),
|
scroll_discrete: [0.0, mouse.scroll_change / 120.0].into(),
|
||||||
raw_input_events: vec![],
|
raw_input_events: vec![],
|
||||||
@@ -131,7 +123,7 @@ impl MousePointer {
|
|||||||
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
|
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
|
||||||
}
|
}
|
||||||
self.target_pointer_input();
|
self.target_pointer_input();
|
||||||
self.send_keyboard_input();
|
self.send_keyboard_input(dbus_connection, object_registry);
|
||||||
}
|
}
|
||||||
fn target_pointer_input(&mut self) {
|
fn target_pointer_input(&mut self) {
|
||||||
let distance_calculator: DistanceCalculator = |space, data, field| {
|
let distance_calculator: DistanceCalculator = |space, data, field| {
|
||||||
@@ -150,64 +142,115 @@ impl MousePointer {
|
|||||||
.set_new_capture(&self.pointer, distance_calculator);
|
.set_new_capture(&self.pointer, distance_calculator);
|
||||||
self.capture_manager.apply_capture(&self.pointer);
|
self.capture_manager.apply_capture(&self.pointer);
|
||||||
|
|
||||||
if self.capture_manager.capture.is_some() {
|
if self.capture_manager.capture.upgrade().is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sorted_handlers = get_sorted_handlers(&self.pointer, distance_calculator);
|
let mut handlers = get_sorted_handlers(&self.pointer, distance_calculator);
|
||||||
self.pointer.set_handler_order(sorted_handlers.iter());
|
let first_distance = handlers
|
||||||
|
.first()
|
||||||
|
.map(|(_, distance)| *distance)
|
||||||
|
.unwrap_or(f32::NEG_INFINITY);
|
||||||
|
|
||||||
|
self.pointer.set_handler_order(
|
||||||
|
handlers
|
||||||
|
.iter()
|
||||||
|
.filter(|(handler, distance)| (distance - first_distance).abs() <= 0.001)
|
||||||
|
.map(|(handler, _)| handler),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_keyboard_input(&mut self) {
|
pub fn send_keyboard_input(
|
||||||
let rx = PULSE_RECEIVER_REGISTRY
|
&mut self,
|
||||||
.get_valid_contents()
|
dbus_connection: &Connection,
|
||||||
.into_iter()
|
object_registry: &ObjectRegistry,
|
||||||
.filter(|rx| mask_matches(&rx.mask, &self.keyboard_sender.mask))
|
) {
|
||||||
.map(|rx| {
|
let keyboard_handlers = object_registry.get_objects("org.stardustxr.XKBv1");
|
||||||
let result = rx.field.ray_march(Ray {
|
|
||||||
origin: vec3(0.0, 0.0, 0.0),
|
// Spawn async task to handle keyboard input
|
||||||
direction: vec3(0.0, 0.0, -1.0),
|
tokio::spawn({
|
||||||
space: self.spatial.clone(),
|
let keyboard_handlers = keyboard_handlers.clone();
|
||||||
});
|
let spatial = self.spatial.clone();
|
||||||
(rx, result)
|
let keymap_id = self.keymap.data().as_ffi();
|
||||||
})
|
let dbus_connection = dbus_connection.clone();
|
||||||
.filter(|(_rx, result)| {
|
|
||||||
result.deepest_point_distance > 0.0 && result.min_distance < 0.05
|
async move {
|
||||||
})
|
let mut closest_handler = None;
|
||||||
.reduce(|(rx_a, result_a), (rx_b, result_b)| {
|
let mut closest_distance = f32::MAX;
|
||||||
if result_a.deepest_point_distance < result_b.deepest_point_distance {
|
|
||||||
(rx_a, result_a)
|
let mut join_set = JoinSet::new();
|
||||||
} else {
|
for handler in &keyboard_handlers {
|
||||||
(rx_b, result_b)
|
let handler = handler.clone();
|
||||||
|
let dbus_connection = dbus_connection.clone();
|
||||||
|
join_set.spawn(async move {
|
||||||
|
timeout(Duration::from_millis(1), async {
|
||||||
|
let field_ref = handler
|
||||||
|
.to_typed_proxy::<FieldRefProxy>(&dbus_connection)
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
let uid = field_ref.uid().await.ok()?;
|
||||||
|
Some((handler, uid))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
while let Some(Ok(Some((handler, field_ref_id)))) = join_set.join_next().await {
|
||||||
.map(|(rx, _)| rx);
|
let exported_fields = EXPORTED_FIELDS.lock();
|
||||||
|
let Some(field_ref_node) = exported_fields.get(&field_ref_id) else {
|
||||||
|
println!("didn't find a thing :(");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// println!("still sendin stuff :)");
|
||||||
|
let Ok(field_ref) = field_ref_node.get_aspect::<Field>() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
drop(exported_fields);
|
||||||
|
|
||||||
if let Some(rx) = rx {
|
let result = field_ref.ray_march(Ray {
|
||||||
let keys = (8_u32..254)
|
origin: vec3(0.0, 0.0, 0.0),
|
||||||
.map(|i| unsafe { std::mem::transmute(i) })
|
direction: vec3(0.0, 0.0, -1.0),
|
||||||
.filter_map(|k| Some((map_key(k)?, Input::key(k))))
|
space: spatial.clone(),
|
||||||
.filter_map(|(i, k)| {
|
});
|
||||||
if k.is_just_active() {
|
|
||||||
Some(i as i32)
|
if result.deepest_point_distance > 0.0
|
||||||
} else if k.is_just_inactive() {
|
&& result.min_distance < 0.05
|
||||||
Some(-(i as i32))
|
&& result.deepest_point_distance < closest_distance
|
||||||
} else {
|
{
|
||||||
None
|
closest_distance = result.deepest_point_distance;
|
||||||
|
closest_handler = Some(handler);
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.keyboard_datamap.keys = keys;
|
let Some(handler) = closest_handler else {
|
||||||
if !self.keyboard_datamap.keys.is_empty() {
|
return;
|
||||||
pulse_receiver_client::data(
|
};
|
||||||
&rx.node.upgrade().unwrap(),
|
let Ok(keyboard_handler) = handler
|
||||||
&self.node.0,
|
.to_typed_proxy::<KeyboardHandlerProxy>(&dbus_connection)
|
||||||
&Datamap::from_typed(&self.keyboard_datamap).unwrap(),
|
.await
|
||||||
)
|
else {
|
||||||
.unwrap();
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register keymap first
|
||||||
|
let _ = keyboard_handler.keymap(keymap_id).await;
|
||||||
|
|
||||||
|
// Send key states
|
||||||
|
for i in 8_u32..254 {
|
||||||
|
let key = unsafe { std::mem::transmute::<u32, stereokit_rust::system::Key>(i) };
|
||||||
|
let Some(mapped_key) = map_key(key) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let input_state = Input::key(key);
|
||||||
|
if input_state.is_just_active() {
|
||||||
|
let _ = keyboard_handler.key_state(mapped_key + 8, true).await;
|
||||||
|
} else if input_state.is_just_inactive() {
|
||||||
|
let _ = keyboard_handler.key_state(mapped_key + 8, false).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use super::{get_sorted_handlers, CaptureManager};
|
use super::{CaptureManager, get_sorted_handlers};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
fields::{Field, FieldTrait},
|
|
||||||
input::{InputDataType, InputHandler, InputMethod, Tip, INPUT_HANDLER_REGISTRY},
|
|
||||||
spatial::Spatial,
|
|
||||||
Node, OwnedNode,
|
Node, OwnedNode,
|
||||||
|
fields::{Field, FieldTrait},
|
||||||
|
input::{INPUT_HANDLER_REGISTRY, InputDataType, InputHandler, InputMethod, Tip},
|
||||||
|
spatial::Spatial,
|
||||||
},
|
},
|
||||||
objects::{ObjectHandle, SpatialRef},
|
objects::{ObjectHandle, SpatialRef, Tracked},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
@@ -40,18 +40,19 @@ pub struct SkController {
|
|||||||
material: Material,
|
material: Material,
|
||||||
capture_manager: CaptureManager,
|
capture_manager: CaptureManager,
|
||||||
datamap: ControllerDatamap,
|
datamap: ControllerDatamap,
|
||||||
|
tracked: ObjectHandle<Tracked>,
|
||||||
}
|
}
|
||||||
impl SkController {
|
impl SkController {
|
||||||
pub fn new(connection: &Connection, handed: Handed) -> Result<Self> {
|
pub fn new(connection: &Connection, handed: Handed) -> Result<Self> {
|
||||||
let (spatial, object_handle) = SpatialRef::create(
|
Input::set_controller_model(handed, Some(Model::new()));
|
||||||
connection,
|
let path = "/org/stardustxr/Controller/".to_string()
|
||||||
&("/org/stardustxr/Controller/".to_string()
|
+ match handed {
|
||||||
+ match handed {
|
Handed::Left => "left",
|
||||||
Handed::Left => "left",
|
_ => "right",
|
||||||
_ => "right",
|
};
|
||||||
}),
|
let (spatial, object_handle) = SpatialRef::create(connection, &path);
|
||||||
);
|
let tracked = Tracked::new(connection, &path);
|
||||||
let model = Model::copy(Model::from_memory(
|
let model = Model::copy(&Model::from_memory(
|
||||||
"cursor.glb",
|
"cursor.glb",
|
||||||
include_bytes!("cursor.glb"),
|
include_bytes!("cursor.glb"),
|
||||||
None,
|
None,
|
||||||
@@ -74,19 +75,28 @@ impl SkController {
|
|||||||
material,
|
material,
|
||||||
capture_manager: CaptureManager::default(),
|
capture_manager: CaptureManager::default(),
|
||||||
datamap: Default::default(),
|
datamap: Default::default(),
|
||||||
|
tracked,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, token: &MainThreadToken) {
|
pub fn update(&mut self, token: &MainThreadToken) {
|
||||||
let controller = Input::controller(self.handed);
|
let controller = Input::controller(self.handed);
|
||||||
let input_node = self.input.spatial.node().unwrap();
|
let input_node = self.input.spatial.node().unwrap();
|
||||||
input_node.set_enabled(controller.tracked.is_active());
|
input_node.set_enabled(controller.tracked.is_active());
|
||||||
if input_node.enabled() {
|
let enabled = input_node.enabled();
|
||||||
|
tokio::spawn({
|
||||||
|
// this is suboptimal since it probably allocates a fresh string every frame
|
||||||
|
let handle = self.tracked.clone();
|
||||||
|
async move {
|
||||||
|
handle.set_tracked(enabled).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if enabled {
|
||||||
let world_transform = Mat4::from_rotation_translation(
|
let world_transform = Mat4::from_rotation_translation(
|
||||||
controller.aim.orientation.into(),
|
controller.aim.orientation.into(),
|
||||||
controller.aim.position.into(),
|
controller.aim.position.into(),
|
||||||
);
|
);
|
||||||
self.material
|
self.material
|
||||||
.color_tint(if self.capture_manager.capture.is_none() {
|
.color_tint(if self.capture_manager.capture.upgrade().is_none() {
|
||||||
Color128::new_rgb(1.0, 1.0, 1.0)
|
Color128::new_rgb(1.0, 1.0, 1.0)
|
||||||
} else {
|
} else {
|
||||||
Color128::new_rgb(0.0, 1.0, 0.75)
|
Color128::new_rgb(0.0, 1.0, 0.75)
|
||||||
@@ -118,11 +128,12 @@ impl SkController {
|
|||||||
.set_new_capture(&self.input, distance_calculator);
|
.set_new_capture(&self.input, distance_calculator);
|
||||||
self.capture_manager.apply_capture(&self.input);
|
self.capture_manager.apply_capture(&self.input);
|
||||||
|
|
||||||
if self.capture_manager.capture.is_some() {
|
if self.capture_manager.capture.upgrade().is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sorted_handlers = get_sorted_handlers(&self.input, distance_calculator);
|
let sorted_handlers = get_sorted_handlers(&self.input, distance_calculator);
|
||||||
self.input.set_handler_order(sorted_handlers.iter());
|
self.input
|
||||||
|
.set_handler_order(sorted_handlers.iter().map(|(handler, _)| handler));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
use crate::core::client::INTERNAL_CLIENT;
|
use crate::core::client::INTERNAL_CLIENT;
|
||||||
use crate::nodes::fields::{Field, FieldTrait};
|
|
||||||
use crate::nodes::input::{InputDataType, InputHandler, INPUT_HANDLER_REGISTRY};
|
|
||||||
use crate::nodes::OwnedNode;
|
use crate::nodes::OwnedNode;
|
||||||
|
use crate::nodes::fields::{Field, FieldTrait};
|
||||||
|
use crate::nodes::input::{INPUT_HANDLER_REGISTRY, InputDataType, InputHandler};
|
||||||
use crate::nodes::{
|
use crate::nodes::{
|
||||||
|
Node,
|
||||||
input::{Hand, InputMethod, Joint},
|
input::{Hand, InputMethod, Joint},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Node,
|
|
||||||
};
|
};
|
||||||
use crate::objects::{ObjectHandle, SpatialRef};
|
use crate::objects::{ObjectHandle, SpatialRef, Tracked};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{Mat4, Quat, Vec3};
|
use glam::{Mat4, Quat, Vec3};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
use std::f32::INFINITY;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use stereokit_rust::material::Material;
|
||||||
use stereokit_rust::sk::{DisplayMode, MainThreadToken, Sk};
|
use stereokit_rust::sk::{DisplayMode, MainThreadToken, Sk};
|
||||||
use stereokit_rust::system::{HandJoint, HandSource, Handed, Input, LinePoint, Lines};
|
use stereokit_rust::system::{HandJoint, HandSource, Handed, Input, LinePoint, Lines};
|
||||||
use stereokit_rust::util::Color128;
|
use stereokit_rust::util::Color128;
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
use super::{get_sorted_handlers, CaptureManager};
|
use super::{CaptureManager, get_sorted_handlers};
|
||||||
|
|
||||||
fn convert_joint(joint: HandJoint) -> Joint {
|
fn convert_joint(joint: HandJoint) -> Joint {
|
||||||
Joint {
|
Joint {
|
||||||
@@ -44,6 +44,7 @@ pub struct SkHand {
|
|||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
capture_manager: CaptureManager,
|
capture_manager: CaptureManager,
|
||||||
datamap: HandDatamap,
|
datamap: HandDatamap,
|
||||||
|
tracked: ObjectHandle<Tracked>,
|
||||||
}
|
}
|
||||||
impl SkHand {
|
impl SkHand {
|
||||||
pub fn new(connection: &Connection, handed: Handed) -> Result<Self> {
|
pub fn new(connection: &Connection, handed: Handed) -> Result<Self> {
|
||||||
@@ -55,6 +56,14 @@ impl SkHand {
|
|||||||
_ => "right",
|
_ => "right",
|
||||||
} + "/palm"),
|
} + "/palm"),
|
||||||
);
|
);
|
||||||
|
let tracked = Tracked::new(
|
||||||
|
connection,
|
||||||
|
&("/org/stardustxr/Hand/".to_string()
|
||||||
|
+ match handed {
|
||||||
|
Handed::Left => "left",
|
||||||
|
_ => "right",
|
||||||
|
}),
|
||||||
|
);
|
||||||
let _node = Node::generate(&INTERNAL_CLIENT, false).add_to_scenegraph_owned()?;
|
let _node = Node::generate(&INTERNAL_CLIENT, false).add_to_scenegraph_owned()?;
|
||||||
Spatial::add_to(&_node.0, None, Mat4::IDENTITY, false);
|
Spatial::add_to(&_node.0, None, Mat4::IDENTITY, false);
|
||||||
let hand = InputDataType::Hand(Hand {
|
let hand = InputDataType::Hand(Hand {
|
||||||
@@ -63,19 +72,20 @@ impl SkHand {
|
|||||||
});
|
});
|
||||||
let datamap = Datamap::from_typed(HandDatamap::default())?;
|
let datamap = Datamap::from_typed(HandDatamap::default())?;
|
||||||
let input = InputMethod::add_to(&_node.0, hand, datamap)?;
|
let input = InputMethod::add_to(&_node.0, hand, datamap)?;
|
||||||
|
Input::hand_visible(handed, true);
|
||||||
|
|
||||||
Input::hand_visible(handed, false);
|
|
||||||
Ok(SkHand {
|
Ok(SkHand {
|
||||||
_node,
|
_node,
|
||||||
palm_spatial,
|
palm_spatial,
|
||||||
palm_object,
|
palm_object,
|
||||||
handed,
|
handed,
|
||||||
input,
|
input,
|
||||||
|
tracked,
|
||||||
capture_manager: CaptureManager::default(),
|
capture_manager: CaptureManager::default(),
|
||||||
datamap: Default::default(),
|
datamap: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, sk: &Sk, token: &MainThreadToken) {
|
pub fn update(&mut self, sk: &Sk, token: &MainThreadToken, material: &mut Material) {
|
||||||
let sk_hand = Input::hand(self.handed);
|
let sk_hand = Input::hand(self.handed);
|
||||||
let real_hand = Input::hand_source(self.handed) as u32 == HandSource::Articulated as u32;
|
let real_hand = Input::hand_source(self.handed) as u32 == HandSource::Articulated as u32;
|
||||||
if let InputDataType::Hand(hand) = &mut *self.input.data.lock() {
|
if let InputDataType::Hand(hand) = &mut *self.input.data.lock() {
|
||||||
@@ -84,7 +94,15 @@ impl SkHand {
|
|||||||
(real_hand || sk.get_active_display_mode() == DisplayMode::Flatscreen)
|
(real_hand || sk.get_active_display_mode() == DisplayMode::Flatscreen)
|
||||||
&& sk_hand.tracked.is_active(),
|
&& sk_hand.tracked.is_active(),
|
||||||
);
|
);
|
||||||
if input_node.enabled() {
|
let enabled = input_node.enabled();
|
||||||
|
tokio::spawn({
|
||||||
|
// this is suboptimal since it probably allocates a fresh string every frame
|
||||||
|
let handle = self.tracked.clone();
|
||||||
|
async move {
|
||||||
|
handle.set_tracked(enabled).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if enabled {
|
||||||
hand.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
|
hand.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
|
||||||
hand.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
|
hand.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
|
||||||
hand.thumb.proximal = convert_joint(sk_hand.fingers[0][2]);
|
hand.thumb.proximal = convert_joint(sk_hand.fingers[0][2]);
|
||||||
@@ -122,15 +140,12 @@ impl SkHand {
|
|||||||
|
|
||||||
hand.elbow = None;
|
hand.elbow = None;
|
||||||
|
|
||||||
self.draw(
|
let hand_color = if self.capture_manager.capture.upgrade().is_none() {
|
||||||
token,
|
Color128::new_rgb(1.0, 1.0, 1.0)
|
||||||
if self.capture_manager.capture.is_none() {
|
} else {
|
||||||
Color128::new_rgb(1.0, 1.0, 1.0)
|
Color128::new_rgb(0.0, 1.0, 0.75)
|
||||||
} else {
|
};
|
||||||
Color128::new_rgb(0.0, 1.0, 0.75)
|
material.color_tint(hand_color);
|
||||||
},
|
|
||||||
hand,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
||||||
@@ -159,72 +174,18 @@ impl SkHand {
|
|||||||
.set_new_capture(&self.input, distance_calculator);
|
.set_new_capture(&self.input, distance_calculator);
|
||||||
self.capture_manager.apply_capture(&self.input);
|
self.capture_manager.apply_capture(&self.input);
|
||||||
|
|
||||||
if self.capture_manager.capture.is_some() {
|
if self.capture_manager.capture.upgrade().is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sorted_handlers = get_sorted_handlers(&self.input, distance_calculator);
|
let sorted_handlers = get_sorted_handlers(&self.input, distance_calculator);
|
||||||
self.input.set_handler_order(sorted_handlers.iter());
|
self.input
|
||||||
|
.set_handler_order(sorted_handlers.iter().map(|(handler, _)| handler));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn draw(&self, token: &MainThreadToken, color: Color128, hand: &Hand) {
|
impl Drop for SkHand {
|
||||||
// thumb
|
fn drop(&mut self) {
|
||||||
Lines::add_list(
|
Input::hand_visible(self.handed, false);
|
||||||
token,
|
|
||||||
&[
|
|
||||||
joint_to_line_point(&hand.thumb.tip, color),
|
|
||||||
joint_to_line_point(&hand.thumb.distal, color),
|
|
||||||
joint_to_line_point(&hand.thumb.proximal, color),
|
|
||||||
joint_to_line_point(&hand.thumb.metacarpal, color),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// index
|
|
||||||
Lines::add_list(
|
|
||||||
token,
|
|
||||||
&[
|
|
||||||
joint_to_line_point(&hand.index.tip, color),
|
|
||||||
joint_to_line_point(&hand.index.distal, color),
|
|
||||||
joint_to_line_point(&hand.index.intermediate, color),
|
|
||||||
joint_to_line_point(&hand.index.proximal, color),
|
|
||||||
joint_to_line_point(&hand.index.metacarpal, color),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// middle
|
|
||||||
Lines::add_list(
|
|
||||||
token,
|
|
||||||
&[
|
|
||||||
joint_to_line_point(&hand.middle.tip, color),
|
|
||||||
joint_to_line_point(&hand.middle.distal, color),
|
|
||||||
joint_to_line_point(&hand.middle.intermediate, color),
|
|
||||||
joint_to_line_point(&hand.middle.proximal, color),
|
|
||||||
joint_to_line_point(&hand.middle.metacarpal, color),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// ring
|
|
||||||
Lines::add_list(
|
|
||||||
token,
|
|
||||||
&[
|
|
||||||
joint_to_line_point(&hand.ring.tip, color),
|
|
||||||
joint_to_line_point(&hand.ring.distal, color),
|
|
||||||
joint_to_line_point(&hand.ring.intermediate, color),
|
|
||||||
joint_to_line_point(&hand.ring.proximal, color),
|
|
||||||
joint_to_line_point(&hand.ring.metacarpal, color),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// palm
|
|
||||||
Lines::add_list(
|
|
||||||
token,
|
|
||||||
&[
|
|
||||||
joint_to_line_point(&hand.wrist, color),
|
|
||||||
joint_to_line_point(&hand.thumb.metacarpal, color),
|
|
||||||
joint_to_line_point(&hand.index.metacarpal, color),
|
|
||||||
joint_to_line_point(&hand.middle.metacarpal, color),
|
|
||||||
joint_to_line_point(&hand.ring.metacarpal, color),
|
|
||||||
joint_to_line_point(&hand.little.metacarpal, color),
|
|
||||||
joint_to_line_point(&hand.wrist, color),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,29 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
fields::{Field, Shape, EXPORTED_FIELDS},
|
|
||||||
spatial::{Spatial, EXPORTED_SPATIALS},
|
|
||||||
Node, OwnedNode,
|
Node, OwnedNode,
|
||||||
|
fields::{EXPORTED_FIELDS, Field, Shape},
|
||||||
|
spatial::{EXPORTED_SPATIALS, Spatial},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use glam::{vec3, Mat4};
|
use glam::{Mat4, vec3};
|
||||||
use input::{
|
use input::{
|
||||||
eye_pointer::EyePointer, mouse_pointer::MousePointer, sk_controller::SkController,
|
eye_pointer::EyePointer, mouse_pointer::MousePointer, sk_controller::SkController,
|
||||||
sk_hand::SkHand,
|
sk_hand::SkHand,
|
||||||
};
|
};
|
||||||
use play_space::PlaySpaceBounds;
|
use play_space::PlaySpaceBounds;
|
||||||
use std::{marker::PhantomData, sync::Arc};
|
use stardust_xr::schemas::dbus::object_registry::ObjectRegistry;
|
||||||
|
use std::{
|
||||||
|
marker::PhantomData,
|
||||||
|
sync::{Arc, atomic::Ordering},
|
||||||
|
};
|
||||||
use stereokit_rust::{
|
use stereokit_rust::{
|
||||||
|
material::Material,
|
||||||
sk::{DisplayMode, MainThreadToken, Sk},
|
sk::{DisplayMode, MainThreadToken, Sk},
|
||||||
system::{Handed, Input, Key, World},
|
system::{Handed, Input, Key, World},
|
||||||
util::Device,
|
util::Device,
|
||||||
};
|
};
|
||||||
use zbus::{interface, object_server::Interface, zvariant::OwnedObjectPath, Connection};
|
use zbus::{Connection, interface, object_server::Interface, zvariant::OwnedObjectPath};
|
||||||
|
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod play_space;
|
pub mod play_space;
|
||||||
@@ -45,6 +50,7 @@ pub struct ServerObjects {
|
|||||||
connection: Connection,
|
connection: Connection,
|
||||||
hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>),
|
hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>),
|
||||||
play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>,
|
play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>,
|
||||||
|
hand_materials: [Material; 2],
|
||||||
inputs: Inputs,
|
inputs: Inputs,
|
||||||
disable_controllers: bool,
|
disable_controllers: bool,
|
||||||
disable_hands: bool,
|
disable_hands: bool,
|
||||||
@@ -53,15 +59,13 @@ impl ServerObjects {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
sk: &Sk,
|
sk: &Sk,
|
||||||
|
hand_materials: [Material; 2],
|
||||||
disable_controllers: bool,
|
disable_controllers: bool,
|
||||||
disable_hands: bool,
|
disable_hands: bool,
|
||||||
) -> ServerObjects {
|
) -> ServerObjects {
|
||||||
let hmd = SpatialRef::create(&connection, "/org/stardustxr/HMD");
|
let hmd = SpatialRef::create(&connection, "/org/stardustxr/HMD");
|
||||||
|
|
||||||
let play_space = (World::has_bounds()
|
let play_space = Some(SpatialRef::create(&connection, "/org/stardustxr/PlaySpace"));
|
||||||
&& World::get_bounds_size().x != 0.0
|
|
||||||
&& World::get_bounds_size().y != 0.0)
|
|
||||||
.then(|| SpatialRef::create(&connection, "/org/stardustxr/PlaySpace"));
|
|
||||||
if play_space.is_some() {
|
if play_space.is_some() {
|
||||||
let dbus_connection = connection.clone();
|
let dbus_connection = connection.clone();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
@@ -106,13 +110,20 @@ impl ServerObjects {
|
|||||||
connection,
|
connection,
|
||||||
hmd,
|
hmd,
|
||||||
play_space,
|
play_space,
|
||||||
|
hand_materials,
|
||||||
inputs,
|
inputs,
|
||||||
disable_controllers,
|
disable_controllers,
|
||||||
disable_hands,
|
disable_hands,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, sk: &Sk, token: &MainThreadToken) {
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
sk: &Sk,
|
||||||
|
token: &MainThreadToken,
|
||||||
|
dbus_connection: &Connection,
|
||||||
|
object_registry: &ObjectRegistry,
|
||||||
|
) {
|
||||||
let hmd_pose = Input::get_head();
|
let hmd_pose = Input::get_head();
|
||||||
self.hmd
|
self.hmd
|
||||||
.0
|
.0
|
||||||
@@ -132,6 +143,7 @@ impl ServerObjects {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
if sk.get_active_display_mode() != DisplayMode::MixedReality {
|
if sk.get_active_display_mode() != DisplayMode::MixedReality {
|
||||||
if Input::key(Key::F6).is_just_inactive() {
|
if Input::key(Key::F6).is_just_inactive() {
|
||||||
self.inputs = Inputs::MousePointer(MousePointer::new().unwrap());
|
self.inputs = Inputs::MousePointer(MousePointer::new().unwrap());
|
||||||
@@ -142,12 +154,12 @@ impl ServerObjects {
|
|||||||
// SkController::new(Handed::Right).unwrap(),
|
// SkController::new(Handed::Right).unwrap(),
|
||||||
// ));
|
// ));
|
||||||
// }
|
// }
|
||||||
if Input::key(Key::F8).is_just_inactive() {
|
// if Input::key(Key::F8).is_just_inactive() {
|
||||||
self.inputs = Inputs::Hands {
|
// self.inputs = Inputs::Hands {
|
||||||
left: SkHand::new(&self.connection, Handed::Left).unwrap(),
|
// left: SkHand::new(&self.connection, Handed::Left).unwrap(),
|
||||||
right: SkHand::new(&self.connection, Handed::Right).unwrap(),
|
// right: SkHand::new(&self.connection, Handed::Right).unwrap(),
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
match &mut self.inputs {
|
match &mut self.inputs {
|
||||||
@@ -162,28 +174,38 @@ impl ServerObjects {
|
|||||||
controller_left.update(token);
|
controller_left.update(token);
|
||||||
controller_right.update(token);
|
controller_right.update(token);
|
||||||
}
|
}
|
||||||
|
Input::hand_visible(Handed::Left, !self.disable_hands);
|
||||||
|
Input::hand_visible(Handed::Right, !self.disable_hands);
|
||||||
if !self.disable_hands {
|
if !self.disable_hands {
|
||||||
hand_left.update(sk, token);
|
hand_left.update(sk, token, &mut self.hand_materials[0]);
|
||||||
hand_right.update(sk, token);
|
hand_right.update(sk, token, &mut self.hand_materials[1]);
|
||||||
}
|
}
|
||||||
if let Some(eye_pointer) = eye_pointer {
|
if let Some(eye_pointer) = eye_pointer {
|
||||||
eye_pointer.update();
|
eye_pointer.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Inputs::MousePointer(mouse_pointer) => mouse_pointer.update(),
|
Inputs::MousePointer(mouse_pointer) => {
|
||||||
|
mouse_pointer.update(dbus_connection, object_registry)
|
||||||
|
}
|
||||||
// Inputs::Controllers((left, right)) => {
|
// Inputs::Controllers((left, right)) => {
|
||||||
// left.update(token);
|
// left.update(token);
|
||||||
// right.update(token);
|
// right.update(token);
|
||||||
// }
|
// }
|
||||||
Inputs::Hands { left, right } => {
|
Inputs::Hands { left, right } => {
|
||||||
left.update(sk, token);
|
left.update(sk, token, &mut self.hand_materials[0]);
|
||||||
right.update(sk, token);
|
right.update(sk, token, &mut self.hand_materials[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ObjectHandle<I: Interface>(Connection, OwnedObjectPath, PhantomData<I>);
|
pub struct ObjectHandle<I: Interface>(Connection, OwnedObjectPath, PhantomData<I>);
|
||||||
|
|
||||||
|
impl<I: Interface> Clone for ObjectHandle<I> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone(), self.1.clone(), PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<I: Interface> Drop for ObjectHandle<I> {
|
impl<I: Interface> Drop for ObjectHandle<I> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let connection = self.0.clone();
|
let connection = self.0.clone();
|
||||||
@@ -231,6 +253,52 @@ impl SpatialRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Tracked(bool);
|
||||||
|
impl Tracked {
|
||||||
|
pub fn new(connection: &Connection, path: &str) -> ObjectHandle<Tracked> {
|
||||||
|
tokio::task::spawn({
|
||||||
|
let connection = connection.clone();
|
||||||
|
let path = path.to_string();
|
||||||
|
async move {
|
||||||
|
connection
|
||||||
|
.object_server()
|
||||||
|
.at(path, Self(false))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ObjectHandle(
|
||||||
|
connection.clone(),
|
||||||
|
OwnedObjectPath::try_from(path.to_string()).unwrap(),
|
||||||
|
PhantomData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ObjectHandle<Tracked> {
|
||||||
|
pub async fn set_tracked(&self, is_tracked: bool) -> zbus::Result<()> {
|
||||||
|
let tracked_ref = self
|
||||||
|
.0
|
||||||
|
.object_server()
|
||||||
|
.interface::<_, Tracked>(self.1.as_ref())
|
||||||
|
.await?;
|
||||||
|
let mut tracked = tracked_ref.get_mut().await;
|
||||||
|
if tracked.0 != is_tracked {
|
||||||
|
tracked.0 = is_tracked;
|
||||||
|
tracked
|
||||||
|
.is_tracked_changed(tracked_ref.signal_emitter())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[interface(name = "org.stardustxr.Tracked")]
|
||||||
|
impl Tracked {
|
||||||
|
#[zbus(property)]
|
||||||
|
fn is_tracked(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FieldRef(u64, OwnedNode);
|
pub struct FieldRef(u64, OwnedNode);
|
||||||
impl FieldRef {
|
impl FieldRef {
|
||||||
pub fn create(
|
pub fn create(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use stereokit_rust::system::World;
|
use stereokit_rust::system::World;
|
||||||
use zbus::{interface, Connection, ObjectServer};
|
use zbus::{Connection, ObjectServer, interface};
|
||||||
|
|
||||||
pub struct PlaySpaceBounds;
|
pub struct PlaySpaceBounds;
|
||||||
impl PlaySpaceBounds {
|
impl PlaySpaceBounds {
|
||||||
@@ -15,12 +15,19 @@ impl PlaySpaceBounds {
|
|||||||
impl PlaySpaceBounds {
|
impl PlaySpaceBounds {
|
||||||
#[zbus(property)]
|
#[zbus(property)]
|
||||||
fn bounds(&self) -> Vec<(f64, f64)> {
|
fn bounds(&self) -> Vec<(f64, f64)> {
|
||||||
let bounds = World::get_bounds_size();
|
if (World::has_bounds()
|
||||||
vec![
|
&& World::get_bounds_size().x != 0.0
|
||||||
((bounds.x).into(), (bounds.y).into()),
|
&& World::get_bounds_size().y != 0.0)
|
||||||
((bounds.x).into(), (-bounds.y).into()),
|
{
|
||||||
((-bounds.x).into(), (-bounds.y).into()),
|
let bounds = World::get_bounds_size();
|
||||||
((-bounds.x).into(), (bounds.y).into()),
|
vec![
|
||||||
]
|
((bounds.x).into(), (bounds.y).into()),
|
||||||
|
((bounds.x).into(), (-bounds.y).into()),
|
||||||
|
((-bounds.x).into(), (-bounds.y).into()),
|
||||||
|
((-bounds.x).into(), (bounds.y).into()),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,11 +104,7 @@ pub fn connection_env() -> FxHashMap<String, String> {
|
|||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
{
|
{
|
||||||
var_env_insert!(env, WAYLAND_DISPLAY);
|
var_env_insert!(env, WAYLAND_DISPLAY);
|
||||||
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
env.insert("XDG_SESSION_TYPE".to_string(), "wayland".to_string());
|
||||||
env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string());
|
|
||||||
env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
|
|
||||||
env.insert("CLUTTER_BACKEND".to_string(), "wayland".to_string());
|
|
||||||
env.insert("SDL_VIDEODRIVER".to_string(), "wayland".to_string());
|
|
||||||
}
|
}
|
||||||
env
|
env
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,20 @@ use crate::{
|
|||||||
wayland::surface::CoreSurface,
|
wayland::surface::CoreSurface,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::{AtomicU32, Ordering};
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::utils::{on_commit_buffer_handler, RendererSurfaceStateUserData},
|
backend::renderer::utils::{RendererSurfaceStateUserData, on_commit_buffer_handler},
|
||||||
delegate_compositor,
|
delegate_compositor,
|
||||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
desktop::PopupKind,
|
||||||
|
reexports::wayland_server::{Client, protocol::wl_surface::WlSurface},
|
||||||
wayland::compositor::{
|
wayland::compositor::{
|
||||||
self, add_post_commit_hook, CompositorClientState, CompositorHandler, CompositorState,
|
CompositorClientState, CompositorHandler, CompositorState, add_post_commit_hook,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
|
pub struct ConfiguredSurface;
|
||||||
|
|
||||||
impl CompositorHandler for WaylandState {
|
impl CompositorHandler for WaylandState {
|
||||||
fn compositor_state(&mut self) -> &mut CompositorState {
|
fn compositor_state(&mut self) -> &mut CompositorState {
|
||||||
@@ -30,19 +32,27 @@ impl CompositorHandler for WaylandState {
|
|||||||
debug!(?surface, "Surface commit");
|
debug!(?surface, "Surface commit");
|
||||||
|
|
||||||
on_commit_buffer_handler::<WaylandState>(surface);
|
on_commit_buffer_handler::<WaylandState>(surface);
|
||||||
let mut count = 0;
|
|
||||||
compositor::with_states(surface, |data| {
|
|
||||||
let count_new = data
|
|
||||||
.data_map
|
|
||||||
.insert_if_missing_threadsafe(|| AtomicU32::new(0));
|
|
||||||
if !count_new {
|
|
||||||
if let Some(stored_count) = data.data_map.get::<AtomicU32>() {
|
|
||||||
count = stored_count.fetch_add(1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
if let Some(toplevel) = self
|
||||||
});
|
.xdg_shell
|
||||||
|
.toplevel_surfaces()
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.wl_surface() == surface)
|
||||||
|
{
|
||||||
|
if !toplevel.is_initial_configure_sent() {
|
||||||
|
debug!("Sending initial configure for toplevel surface");
|
||||||
|
toplevel.send_configure();
|
||||||
|
surface.insert_data(ConfiguredSurface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.popup_manager.commit(surface);
|
||||||
|
if let Some(PopupKind::Xdg(popup)) = self.popup_manager.find_popup(surface) {
|
||||||
|
if surface.insert_data(ConfiguredSurface) {
|
||||||
|
debug!("Configuring popup surface");
|
||||||
|
let _ = popup.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
||||||
@@ -54,6 +64,7 @@ impl CompositorHandler for WaylandState {
|
|||||||
surface.insert_data(SurfaceId::Child(id));
|
surface.insert_data(SurfaceId::Child(id));
|
||||||
CoreSurface::add_to(surface);
|
CoreSurface::add_to(surface);
|
||||||
let Some(parent_surface_id) = parent.get_data::<SurfaceId>() else {
|
let Some(parent_surface_id) = parent.get_data::<SurfaceId>() else {
|
||||||
|
warn!("Parent surface has no SurfaceId");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
surface.insert_data(Mutex::new(ChildInfo {
|
surface.insert_data(Mutex::new(ChildInfo {
|
||||||
@@ -68,6 +79,7 @@ impl CompositorHandler for WaylandState {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let Some(panel_item) = surface_panel_item(parent) else {
|
let Some(panel_item) = surface_panel_item(parent) else {
|
||||||
|
warn!("Parent has no panel item");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let panel_item_weak = Arc::downgrade(&panel_item);
|
let panel_item_weak = Arc::downgrade(&panel_item);
|
||||||
@@ -75,11 +87,19 @@ impl CompositorHandler for WaylandState {
|
|||||||
if surface_panel_item(surf).is_some() {
|
if surface_panel_item(surf).is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
debug!("Linking surface to panel item");
|
||||||
surf.insert_data(panel_item_weak.clone());
|
surf.insert_data(panel_item_weak.clone());
|
||||||
|
|
||||||
let Some(panel_item) = surface_panel_item(surf) else {
|
let Some(panel_item) = surface_panel_item(surf) else {
|
||||||
|
warn!("Failed to link surface to panel item");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
surf.with_child_info(|_info| {
|
||||||
|
panel_item.backend.reposition_child(surf);
|
||||||
|
});
|
||||||
|
|
||||||
|
debug!("Adding new child to panel item");
|
||||||
panel_item.backend.new_child(surf);
|
panel_item.backend.new_child(surf);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,6 +108,7 @@ impl CompositorHandler for WaylandState {
|
|||||||
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|s| s.lock().ok()?.view())
|
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|s| s.lock().ok()?.view())
|
||||||
.flatten()
|
.flatten()
|
||||||
else {
|
else {
|
||||||
|
debug!("No view data for surface");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
@@ -96,11 +117,13 @@ impl CompositorHandler for WaylandState {
|
|||||||
&& info.geometry.origin.y != view.offset.y
|
&& info.geometry.origin.y != view.offset.y
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
|
debug!("Surface position changed");
|
||||||
}
|
}
|
||||||
if info.geometry.size.x != view.dst.w as u32
|
if info.geometry.size.x != view.dst.w as u32
|
||||||
&& info.geometry.size.y != view.dst.h as u32
|
&& info.geometry.size.y != view.dst.h as u32
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
|
debug!("Surface size changed");
|
||||||
}
|
}
|
||||||
info.geometry.size = [view.dst.w as u32, view.dst.h as u32].into();
|
info.geometry.size = [view.dst.w as u32, view.dst.h as u32].into();
|
||||||
});
|
});
|
||||||
@@ -109,6 +132,7 @@ impl CompositorHandler for WaylandState {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if changed {
|
if changed {
|
||||||
|
debug!("Repositioning child due to geometry change");
|
||||||
panel_item.backend.reposition_child(surf);
|
panel_item.backend.reposition_child(surf);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -119,6 +143,7 @@ impl CompositorHandler for WaylandState {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if surface.get_child_info().is_some() {
|
if surface.get_child_info().is_some() {
|
||||||
|
debug!("Dropping destroyed child surface");
|
||||||
panel_item.backend.drop_child(surface);
|
panel_item.backend.drop_child(surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use smithay::reexports::wayland_server::{
|
use smithay::reexports::wayland_server::{
|
||||||
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_data_device::{
|
wl_data_device::{
|
||||||
Request::{Release, SetSelection, StartDrag},
|
Request::{Release, SetSelection, StartDrag},
|
||||||
@@ -13,7 +14,6 @@ use smithay::reexports::wayland_server::{
|
|||||||
WlDataSource,
|
WlDataSource,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::state::WaylandState;
|
use super::state::WaylandState;
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ use smithay::{
|
|||||||
Mode as KdeMode, OrgKdeKwinServerDecoration,
|
Mode as KdeMode, OrgKdeKwinServerDecoration,
|
||||||
},
|
},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle,
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum, Weak,
|
||||||
GlobalDispatch, New, Resource, WEnum, Weak,
|
protocol::wl_surface::WlSurface,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wayland::shell::{self, kde::decoration::KdeDecorationHandler},
|
wayland::shell::{self, kde::decoration::KdeDecorationHandler},
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ mod generated {
|
|||||||
use super::state::WaylandState;
|
use super::state::WaylandState;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::allocator::{
|
backend::allocator::{
|
||||||
dmabuf::{Dmabuf, DmabufFlags},
|
|
||||||
Fourcc, Modifier,
|
Fourcc, Modifier,
|
||||||
|
dmabuf::{Dmabuf, DmabufFlags},
|
||||||
},
|
},
|
||||||
reexports::wayland_server::{
|
reexports::wayland_server::{
|
||||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||||
|
|||||||
@@ -11,35 +11,35 @@ mod xdg_shell;
|
|||||||
|
|
||||||
use self::{state::WaylandState, surface::CORE_SURFACES};
|
use self::{state::WaylandState, surface::CORE_SURFACES};
|
||||||
use crate::{core::task, wayland::state::ClientState};
|
use crate::{core::task, wayland::state::ClientState};
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{Result, ensure};
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
use smithay::{
|
||||||
use smithay::backend::egl::EGLContext;
|
backend::{
|
||||||
use smithay::backend::renderer::gles::GlesRenderer;
|
allocator::dmabuf::Dmabuf,
|
||||||
use smithay::backend::renderer::{ImportDma, Renderer};
|
egl::EGLContext,
|
||||||
use smithay::output::Output;
|
renderer::{ImportDma, Renderer, gles::GlesRenderer},
|
||||||
use smithay::reexports::wayland_server::backend::ClientId;
|
},
|
||||||
use smithay::reexports::wayland_server::DisplayHandle;
|
output::Output,
|
||||||
use smithay::reexports::wayland_server::{Display, ListeningSocket};
|
reexports::wayland_server::{Display, DisplayHandle, ListeningSocket},
|
||||||
use smithay::wayland::dmabuf;
|
wayland::dmabuf,
|
||||||
use std::ffi::OsStr;
|
};
|
||||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
|
||||||
use std::os::unix::prelude::AsRawFd;
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::c_void,
|
ffi::{OsStr, c_void},
|
||||||
os::unix::{net::UnixListener, prelude::FromRawFd},
|
os::fd::AsFd,
|
||||||
sync::Arc,
|
sync::{Arc, OnceLock},
|
||||||
};
|
};
|
||||||
use stereokit_rust::system::{Backend, BackendGraphics};
|
use stereokit_rust::system::{Backend, BackendGraphics};
|
||||||
use tokio::io::unix::AsyncFdReadyGuard;
|
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle,
|
io::unix::AsyncFd,
|
||||||
|
sync::{
|
||||||
|
Notify,
|
||||||
|
mpsc::{self, UnboundedReceiver},
|
||||||
|
},
|
||||||
|
task::AbortHandle,
|
||||||
};
|
};
|
||||||
use tracing::{debug_span, info, instrument};
|
use tracing::{debug_span, info, instrument};
|
||||||
|
|
||||||
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
pub static WAYLAND_DISPLAY: OnceLock<String> = OnceLock::new();
|
||||||
|
|
||||||
struct EGLRawHandles {
|
struct EGLRawHandles {
|
||||||
display: *const c_void,
|
display: *const c_void,
|
||||||
@@ -61,40 +61,10 @@ fn get_sk_egl() -> Result<EGLRawHandles> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DisplayWrapper(Mutex<Display<WaylandState>>, DisplayHandle);
|
|
||||||
impl DisplayWrapper {
|
|
||||||
pub fn handle(&self) -> DisplayHandle {
|
|
||||||
self.1.clone()
|
|
||||||
}
|
|
||||||
pub fn dispatch_clients(&self, state: &mut WaylandState) -> Result<usize, std::io::Error> {
|
|
||||||
self.0.lock().dispatch_clients(state)
|
|
||||||
}
|
|
||||||
pub fn flush_clients(&self, client: Option<ClientId>) {
|
|
||||||
if let Some(mut lock) = self.0.try_lock() {
|
|
||||||
let _ = lock.backend().flush(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn poll_fd(&self) -> Result<OwnedFd, std::io::Error> {
|
|
||||||
self.0.lock().backend().poll_fd().try_clone_to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnownedFd(Option<AsyncFd<OwnedFd>>);
|
|
||||||
impl UnownedFd {
|
|
||||||
async fn readable(&self) -> std::io::Result<AsyncFdReadyGuard<'_, OwnedFd>> {
|
|
||||||
self.0.as_ref().unwrap().readable().await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for UnownedFd {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.0.take().unwrap().into_inner().into_raw_fd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Wayland {
|
pub struct Wayland {
|
||||||
display: Arc<DisplayWrapper>,
|
flush_notify: Arc<Notify>,
|
||||||
pub socket_name: Option<String>,
|
client_listener: AbortHandle,
|
||||||
join_handle: JoinHandle<Result<()>>,
|
client_dispatcher: AbortHandle,
|
||||||
renderer: GlesRenderer,
|
renderer: GlesRenderer,
|
||||||
output: Output,
|
output: Output,
|
||||||
dmabuf_rx: UnboundedReceiver<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
dmabuf_rx: UnboundedReceiver<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||||
@@ -114,7 +84,6 @@ impl Wayland {
|
|||||||
let display_handle = display.handle();
|
let display_handle = display.handle();
|
||||||
|
|
||||||
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
||||||
let display = Arc::new(DisplayWrapper(Mutex::new(display), display_handle.clone()));
|
|
||||||
|
|
||||||
let wayland_state = WaylandState::new(display_handle.clone(), &renderer, dmabuf_tx);
|
let wayland_state = WaylandState::new(display_handle.clone(), &renderer, dmabuf_tx);
|
||||||
let output = wayland_state.lock().output.clone();
|
let output = wayland_state.lock().output.clone();
|
||||||
@@ -129,58 +98,78 @@ impl Wayland {
|
|||||||
}
|
}
|
||||||
info!(socket_name, "Wayland active");
|
info!(socket_name, "Wayland active");
|
||||||
|
|
||||||
let join_handle = Wayland::start_loop(display.clone(), socket, wayland_state)?;
|
let flush_notify = Arc::new(Notify::new());
|
||||||
|
let client_listener = task::new(
|
||||||
|
|| "Wayland client listener loop",
|
||||||
|
Wayland::client_listener_loop(display_handle, socket, wayland_state.clone()),
|
||||||
|
)?
|
||||||
|
.abort_handle();
|
||||||
|
let client_dispatcher = task::new(
|
||||||
|
|| "Wayland dispatch client loop",
|
||||||
|
Wayland::dispatch_client_loop(display, flush_notify.clone(), wayland_state),
|
||||||
|
)?
|
||||||
|
.abort_handle();
|
||||||
|
|
||||||
Ok(Wayland {
|
Ok(Wayland {
|
||||||
display,
|
flush_notify,
|
||||||
socket_name,
|
client_listener,
|
||||||
join_handle,
|
client_dispatcher,
|
||||||
renderer,
|
renderer,
|
||||||
output,
|
output,
|
||||||
dmabuf_rx,
|
dmabuf_rx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_loop(
|
async fn client_listener_loop(
|
||||||
display: Arc<DisplayWrapper>,
|
mut display_handle: DisplayHandle,
|
||||||
socket: ListeningSocket,
|
socket: ListeningSocket,
|
||||||
state: Arc<Mutex<WaylandState>>,
|
state: Arc<Mutex<WaylandState>>,
|
||||||
) -> Result<JoinHandle<Result<()>>> {
|
) -> Result<()> {
|
||||||
let listen_async =
|
let async_fd = AsyncFd::new(socket.as_fd())?;
|
||||||
AsyncUnixListener::from_std(unsafe { UnixListener::from_raw_fd(socket.as_raw_fd()) })?;
|
loop {
|
||||||
|
let mut guard = async_fd.readable().await?;
|
||||||
|
let Ok(Some(stream)) = socket.accept() else {
|
||||||
|
guard.clear_ready();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let dispatch_poll_fd = display.poll_fd()?;
|
let stream = tokio::net::UnixStream::from_std(stream)?;
|
||||||
let dispatch_poll_listener = UnownedFd(Some(AsyncFd::new(dispatch_poll_fd)?));
|
let pid = stream.peer_cred().ok().and_then(|c| c.pid());
|
||||||
|
|
||||||
let dh1 = display.handle();
|
// New client connected
|
||||||
let mut dh2 = dh1.clone();
|
let client_state = Arc::new(ClientState {
|
||||||
|
pid,
|
||||||
|
id: OnceLock::new(),
|
||||||
|
compositor_state: Default::default(),
|
||||||
|
seat: state.lock().seat.clone(),
|
||||||
|
});
|
||||||
|
let _client = display_handle.insert_client(stream.into_std()?, client_state.clone())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
task::new(|| "wayland loop", async move {
|
async fn dispatch_client_loop(
|
||||||
let _socket = socket; // Keep the socket alive
|
mut display: Display<WaylandState>,
|
||||||
loop {
|
flush_notify: Arc<Notify>,
|
||||||
tokio::select! {
|
state: Arc<Mutex<WaylandState>>,
|
||||||
acc = listen_async.accept() => { // New client connected
|
) -> std::io::Result<()> {
|
||||||
let (stream, _) = acc?;
|
loop {
|
||||||
let client_state = Arc::new(ClientState {
|
let poll_fd = display.backend().poll_fd();
|
||||||
pid: stream.peer_cred().ok().and_then(|c| c.pid()),
|
let async_fd = AsyncFd::new(poll_fd)?;
|
||||||
id: OnceCell::new(),
|
tokio::select! {
|
||||||
compositor_state: Default::default(),
|
biased;
|
||||||
seat: state.lock().seat.clone(),
|
_ = async_fd.readable() => {
|
||||||
});
|
drop(async_fd);
|
||||||
let _client = dh2.insert_client(stream.into_std()?, client_state.clone())?;
|
let _span = debug_span!("Dispatch wayland event");
|
||||||
}
|
let _span = _span.enter();
|
||||||
e = dispatch_poll_listener.readable() => { // Dispatch
|
let _ = display.dispatch_clients(&mut *state.lock());
|
||||||
let mut guard = e?;
|
let _ = display.flush_clients();
|
||||||
debug_span!("Dispatch wayland event").in_scope(|| -> Result<(), color_eyre::Report> {
|
|
||||||
display.dispatch_clients(&mut state.lock())?;
|
|
||||||
display.flush_clients(None);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
guard.clear_ready();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_ = flush_notify.notified() => {
|
||||||
|
drop(async_fd);
|
||||||
|
let _ = display.flush_clients();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", name = "Wayland frame", skip(self))]
|
#[instrument(level = "debug", name = "Wayland frame", skip(self))]
|
||||||
@@ -197,7 +186,7 @@ impl Wayland {
|
|||||||
}
|
}
|
||||||
let _ = self.renderer.cleanup_texture_cache();
|
let _ = self.renderer.cleanup_texture_cache();
|
||||||
|
|
||||||
self.display.flush_clients(None);
|
self.flush_notify.notify_waiters();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_event(&self) {
|
pub fn frame_event(&self) {
|
||||||
@@ -214,6 +203,7 @@ impl Wayland {
|
|||||||
}
|
}
|
||||||
impl Drop for Wayland {
|
impl Drop for Wayland {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.join_handle.abort();
|
self.client_listener.abort();
|
||||||
|
self.client_dispatcher.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
use super::{state::WaylandState, surface::CoreSurface, utils::WlSurfaceExt};
|
use super::{state::WaylandState, surface::CoreSurface, utils::WlSurfaceExt};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::task,
|
core::task,
|
||||||
nodes::{
|
nodes::items::panel::{Backend, Geometry, KEYMAPS, PanelItem},
|
||||||
data::KEYMAPS,
|
|
||||||
items::panel::{Backend, Geometry, PanelItem},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@@ -14,12 +11,12 @@ use smithay::{
|
|||||||
backend::input::{AxisRelativeDirection, ButtonState, KeyState},
|
backend::input::{AxisRelativeDirection, ButtonState, KeyState},
|
||||||
delegate_seat,
|
delegate_seat,
|
||||||
input::{
|
input::{
|
||||||
keyboard::{FilterResult, LedState},
|
|
||||||
pointer::{AxisFrame, ButtonEvent, CursorImageStatus, MotionEvent},
|
|
||||||
touch::{self, DownEvent, UpEvent},
|
|
||||||
Seat, SeatHandler,
|
Seat, SeatHandler,
|
||||||
|
keyboard::{FilterResult, LedState},
|
||||||
|
pointer::{AxisFrame, ButtonEvent, CursorImageStatus, CursorImageSurfaceData, MotionEvent},
|
||||||
|
touch::{self, DownEvent, UpEvent},
|
||||||
},
|
},
|
||||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource, Weak as WlWeak},
|
reexports::wayland_server::{Resource, Weak as WlWeak, protocol::wl_surface::WlSurface},
|
||||||
utils::SERIAL_COUNTER,
|
utils::SERIAL_COUNTER,
|
||||||
wayland::compositor,
|
wayland::compositor,
|
||||||
};
|
};
|
||||||
@@ -41,6 +38,11 @@ impl SeatHandler for WaylandState {
|
|||||||
CursorImageStatus::Surface(surface) => {
|
CursorImageStatus::Surface(surface) => {
|
||||||
CoreSurface::add_to(&surface);
|
CoreSurface::add_to(&surface);
|
||||||
compositor::with_states(&surface, |data| {
|
compositor::with_states(&surface, |data| {
|
||||||
|
if let Some(cursor_attributes) = data.data_map.get::<CursorImageSurfaceData>() {
|
||||||
|
let hotspot = cursor_attributes.lock().unwrap().hotspot;
|
||||||
|
c.hotspot_x = hotspot.x;
|
||||||
|
c.hotspot_y = hotspot.y;
|
||||||
|
}
|
||||||
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
||||||
core_surface.set_material_offset(1);
|
core_surface.set_material_offset(1);
|
||||||
}
|
}
|
||||||
@@ -106,6 +108,12 @@ impl SeatWrapper {
|
|||||||
touches: Mutex::new(FxHashMap::default()),
|
touches: Mutex::new(FxHashMap::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn unfocus_internal_state(&self, surface: &WlSurface) {
|
||||||
|
let Some(state) = self.wayland_state.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.unfocus(surface, &mut state.lock());
|
||||||
|
}
|
||||||
pub fn unfocus(&self, surface: &WlSurface, state: &mut WaylandState) {
|
pub fn unfocus(&self, surface: &WlSurface, state: &mut WaylandState) {
|
||||||
let pointer = self.seat.get_pointer().unwrap();
|
let pointer = self.seat.get_pointer().unwrap();
|
||||||
if pointer.current_focus() == Some(surface.clone()) {
|
if pointer.current_focus() == Some(surface.clone()) {
|
||||||
@@ -203,7 +211,7 @@ impl SeatWrapper {
|
|||||||
pointer.frame(&mut state);
|
pointer.frame(&mut state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keyboard_keys(&self, surface: WlSurface, keymap_id: u64, keys: Vec<i32>) {
|
pub fn keyboard_key(&self, surface: WlSurface, keymap_id: u64, key: u32, pressed: bool) {
|
||||||
let Some(state) = self.wayland_state.upgrade() else {
|
let Some(state) = self.wayland_state.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -226,20 +234,18 @@ impl SeatWrapper {
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for key in keys {
|
keyboard.input(
|
||||||
keyboard.input(
|
&mut state.lock(),
|
||||||
&mut state.lock(),
|
key.into(),
|
||||||
key.unsigned_abs(),
|
if pressed {
|
||||||
if key > 0 {
|
KeyState::Pressed
|
||||||
KeyState::Pressed
|
} else {
|
||||||
} else {
|
KeyState::Released
|
||||||
KeyState::Released
|
},
|
||||||
},
|
SERIAL_COUNTER.next_serial(),
|
||||||
SERIAL_COUNTER.next_serial(),
|
0,
|
||||||
0,
|
|_, _, _| FilterResult::Forward::<()>,
|
||||||
|_, _, _| FilterResult::Forward::<()>,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn touch_down(&self, surface: WlSurface, id: u32, position: Vector2<f32>) {
|
pub fn touch_down(&self, surface: WlSurface, id: u32, position: Vector2<f32>) {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use super::seat::SeatWrapper;
|
use super::seat::SeatWrapper;
|
||||||
use crate::wayland::drm::wl_drm::WlDrm;
|
use crate::wayland::drm::wl_drm::WlDrm;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::{dmabuf::Dmabuf, Fourcc},
|
allocator::{Fourcc, dmabuf::Dmabuf},
|
||||||
egl::EGLDevice,
|
egl::EGLDevice,
|
||||||
renderer::gles::GlesRenderer,
|
renderer::gles::GlesRenderer,
|
||||||
},
|
},
|
||||||
delegate_dmabuf, delegate_output, delegate_shm,
|
delegate_dmabuf, delegate_output, delegate_shm, delegate_viewporter,
|
||||||
input::{keyboard::XkbConfig, SeatState},
|
desktop::PopupManager,
|
||||||
|
input::{SeatState, keyboard::XkbConfig},
|
||||||
output::{Mode, Output, Scale, Subpixel},
|
output::{Mode, Output, Scale, Subpixel},
|
||||||
reexports::{
|
reexports::{
|
||||||
wayland_protocols::xdg::{
|
wayland_protocols::xdg::{
|
||||||
@@ -18,12 +18,12 @@ use smithay::{
|
|||||||
},
|
},
|
||||||
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode,
|
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode,
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
|
DisplayHandle,
|
||||||
backend::{ClientData, ClientId, DisconnectReason},
|
backend::{ClientData, ClientId, DisconnectReason},
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager,
|
wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager,
|
||||||
wl_output::WlOutput,
|
wl_output::WlOutput,
|
||||||
},
|
},
|
||||||
DisplayHandle,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::{Size, Transform},
|
utils::{Size, Transform},
|
||||||
@@ -39,15 +39,16 @@ use smithay::{
|
|||||||
xdg::{WmCapabilitySet, XdgShellState},
|
xdg::{WmCapabilitySet, XdgShellState},
|
||||||
},
|
},
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
|
viewporter::ViewporterState,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, OnceLock};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub pid: Option<i32>,
|
pub pid: Option<i32>,
|
||||||
pub id: OnceCell<ClientId>,
|
pub id: OnceLock<ClientId>,
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
pub seat: Arc<SeatWrapper>,
|
pub seat: Arc<SeatWrapper>,
|
||||||
}
|
}
|
||||||
@@ -71,11 +72,13 @@ pub struct WaylandState {
|
|||||||
pub kde_decoration_state: KdeDecorationState,
|
pub kde_decoration_state: KdeDecorationState,
|
||||||
pub shm_state: ShmState,
|
pub shm_state: ShmState,
|
||||||
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
||||||
|
pub _viewporter_state: ViewporterState,
|
||||||
pub drm_formats: Vec<Fourcc>,
|
pub drm_formats: Vec<Fourcc>,
|
||||||
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||||
pub seat_state: SeatState<Self>,
|
pub seat_state: SeatState<Self>,
|
||||||
pub seat: Arc<SeatWrapper>,
|
pub seat: Arc<SeatWrapper>,
|
||||||
pub xdg_shell: XdgShellState,
|
pub xdg_shell: XdgShellState,
|
||||||
|
pub popup_manager: PopupManager,
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +161,8 @@ impl WaylandState {
|
|||||||
output.set_preferred(mode);
|
output.set_preferred(mode);
|
||||||
|
|
||||||
let mut xdg_shell = XdgShellState::new::<Self>(&display_handle);
|
let mut xdg_shell = XdgShellState::new::<Self>(&display_handle);
|
||||||
|
let _viewporter_state = ViewporterState::new::<Self>(&display_handle);
|
||||||
|
let popup_manager = PopupManager::default();
|
||||||
let mut capabilities = WmCapabilitySet::default();
|
let mut capabilities = WmCapabilitySet::default();
|
||||||
capabilities.set(WmCapabilities::Maximize);
|
capabilities.set(WmCapabilities::Maximize);
|
||||||
capabilities.set(WmCapabilities::Fullscreen);
|
capabilities.set(WmCapabilities::Fullscreen);
|
||||||
@@ -182,7 +187,9 @@ impl WaylandState {
|
|||||||
seat_state,
|
seat_state,
|
||||||
seat: Arc::new(SeatWrapper::new(weak.clone(), seat)),
|
seat: Arc::new(SeatWrapper::new(weak.clone(), seat)),
|
||||||
xdg_shell,
|
xdg_shell,
|
||||||
|
popup_manager,
|
||||||
output,
|
output,
|
||||||
|
_viewporter_state,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -220,3 +227,4 @@ impl OutputHandler for WaylandState {
|
|||||||
delegate_dmabuf!(WaylandState);
|
delegate_dmabuf!(WaylandState);
|
||||||
delegate_shm!(WaylandState);
|
delegate_shm!(WaylandState);
|
||||||
delegate_output!(WaylandState);
|
delegate_output!(WaylandState);
|
||||||
|
delegate_viewporter!(WaylandState);
|
||||||
|
|||||||
@@ -9,20 +9,23 @@ use crate::{
|
|||||||
items::camera::TexWrapper,
|
items::camera::TexWrapper,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use send_wrapper::SendWrapper;
|
use send_wrapper::SendWrapper;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::renderer::{
|
||||||
gles::{GlesRenderer, GlesTexture},
|
|
||||||
utils::{import_surface_tree, RendererSurfaceStateUserData},
|
|
||||||
Renderer, Texture,
|
Renderer, Texture,
|
||||||
|
gles::{GlesRenderer, GlesTexture},
|
||||||
|
utils::{RendererSurfaceStateUserData, import_surface_tree},
|
||||||
},
|
},
|
||||||
desktop::utils::send_frames_surface_tree,
|
desktop::utils::send_frames_surface_tree,
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::wayland_server::{self, protocol::wl_surface::WlSurface, Resource},
|
reexports::wayland_server::{self, Resource, protocol::wl_surface::WlSurface},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
ffi::c_void,
|
||||||
|
sync::{Arc, OnceLock},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
use std::{ffi::c_void, sync::Arc, time::Duration};
|
|
||||||
use stereokit_rust::{
|
use stereokit_rust::{
|
||||||
material::{Material, Transparency},
|
material::{Material, Transparency},
|
||||||
shader::Shader,
|
shader::Shader,
|
||||||
@@ -44,8 +47,8 @@ impl Drop for CoreSurfaceData {
|
|||||||
pub struct CoreSurface {
|
pub struct CoreSurface {
|
||||||
pub weak_surface: wayland_server::Weak<WlSurface>,
|
pub weak_surface: wayland_server::Weak<WlSurface>,
|
||||||
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
||||||
sk_tex: OnceCell<Mutex<TexWrapper>>,
|
sk_tex: OnceLock<Mutex<TexWrapper>>,
|
||||||
sk_mat: OnceCell<Mutex<MaterialWrapper>>,
|
sk_mat: OnceLock<Mutex<MaterialWrapper>>,
|
||||||
material_offset: Mutex<Delta<u32>>,
|
material_offset: Mutex<Delta<u32>>,
|
||||||
pub pending_material_applications: Registry<ModelPart>,
|
pub pending_material_applications: Registry<ModelPart>,
|
||||||
}
|
}
|
||||||
@@ -55,8 +58,8 @@ impl CoreSurface {
|
|||||||
let core_surface = CORE_SURFACES.add(CoreSurface {
|
let core_surface = CORE_SURFACES.add(CoreSurface {
|
||||||
weak_surface: surface.downgrade(),
|
weak_surface: surface.downgrade(),
|
||||||
mapped_data: Mutex::new(None),
|
mapped_data: Mutex::new(None),
|
||||||
sk_tex: OnceCell::new(),
|
sk_tex: OnceLock::new(),
|
||||||
sk_mat: OnceCell::new(),
|
sk_mat: OnceLock::new(),
|
||||||
material_offset: Mutex::new(Delta::new(0)),
|
material_offset: Mutex::new(Delta::new(0)),
|
||||||
pending_material_applications: Registry::new(),
|
pending_material_applications: Registry::new(),
|
||||||
});
|
});
|
||||||
@@ -92,7 +95,8 @@ impl CoreSurface {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Import all surface buffers into textures
|
// Import all surface buffers into textures
|
||||||
if import_surface_tree(renderer, &wl_surface).is_err() {
|
if let Err(err) = import_surface_tree(renderer, &wl_surface) {
|
||||||
|
tracing::error!("Failed to import surface tree for surface {}: {}", wl_surface.id(), err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,25 +108,11 @@ impl CoreSurface {
|
|||||||
let Some(wl_surface) = self.wl_surface() else {
|
let Some(wl_surface) = self.wl_surface() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mapped = wl_surface
|
|
||||||
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|surface_states| {
|
|
||||||
surface_states.lock().unwrap().buffer().is_some()
|
|
||||||
})
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
if !mapped {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut mapped_data = self.mapped_data.lock();
|
|
||||||
|
|
||||||
let Some(smithay_tex) = wl_surface
|
let Some(smithay_tex) = wl_surface
|
||||||
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|surface_states| {
|
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|surface_states| {
|
||||||
surface_states
|
let locked = surface_states.lock().unwrap();
|
||||||
.lock()
|
locked.texture::<GlesRenderer>(renderer.id()).cloned()
|
||||||
.unwrap()
|
|
||||||
.texture::<GlesRenderer>(renderer.id())
|
|
||||||
.cloned()
|
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
else {
|
else {
|
||||||
@@ -130,9 +120,11 @@ impl CoreSurface {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(sk_tex) = self.sk_tex.get() else {
|
let Some(sk_tex) = self.sk_tex.get() else {
|
||||||
|
tracing::error!("No sk_tex found for surface");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(sk_mat) = self.sk_mat.get() else {
|
let Some(sk_mat) = self.sk_mat.get() else {
|
||||||
|
tracing::error!("No sk_mat found for surface");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
sk_tex
|
sk_tex
|
||||||
@@ -157,7 +149,7 @@ impl CoreSurface {
|
|||||||
let new_mapped_data = CoreSurfaceData {
|
let new_mapped_data = CoreSurfaceData {
|
||||||
wl_tex: Some(SendWrapper::new(smithay_tex)),
|
wl_tex: Some(SendWrapper::new(smithay_tex)),
|
||||||
};
|
};
|
||||||
*mapped_data = Some(new_mapped_data);
|
*self.mapped_data.lock() = Some(new_mapped_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame(&self, output: Output) {
|
pub fn frame(&self, output: Output) {
|
||||||
|
|||||||
@@ -1,30 +1,34 @@
|
|||||||
use super::{
|
use super::{
|
||||||
seat::{handle_cursor, SeatWrapper},
|
seat::{SeatWrapper, handle_cursor},
|
||||||
state::{ClientState, WaylandState},
|
state::{ClientState, WaylandState},
|
||||||
surface::CoreSurface,
|
surface::CoreSurface,
|
||||||
utils::*,
|
utils::*,
|
||||||
};
|
};
|
||||||
use crate::nodes::{
|
use crate::{
|
||||||
drawable::model::ModelPart,
|
core::error::Result,
|
||||||
items::panel::{
|
nodes::{
|
||||||
Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceId, ToplevelInfo,
|
drawable::model::ModelPart,
|
||||||
|
items::panel::{
|
||||||
|
Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceId, ToplevelInfo,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::eyre;
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
delegate_xdg_shell,
|
delegate_xdg_shell,
|
||||||
|
desktop::PopupKind,
|
||||||
reexports::{
|
reexports::{
|
||||||
wayland_protocols::xdg::{
|
wayland_protocols::xdg::{
|
||||||
decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode,
|
decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode,
|
||||||
shell::server::xdg_toplevel::{ResizeEdge, State},
|
shell::server::xdg_toplevel::{ResizeEdge, State},
|
||||||
},
|
},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
|
|
||||||
Resource,
|
Resource,
|
||||||
|
protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::{Logical, Rectangle, Serial},
|
utils::{Logical, Rectangle, Serial},
|
||||||
@@ -87,7 +91,6 @@ impl XdgShellHandler for WaylandState {
|
|||||||
s.states.set(State::Maximized);
|
s.states.set(State::Maximized);
|
||||||
s.states.unset(State::Fullscreen);
|
s.states.unset(State::Fullscreen);
|
||||||
});
|
});
|
||||||
toplevel.send_configure();
|
|
||||||
|
|
||||||
let initial_size = toplevel
|
let initial_size = toplevel
|
||||||
.wl_surface()
|
.wl_surface()
|
||||||
@@ -222,14 +225,17 @@ impl XdgShellHandler for WaylandState {
|
|||||||
};
|
};
|
||||||
panel_item.toplevel_title_changed(&title)
|
panel_item.toplevel_title_changed(&title)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_popup(&mut self, popup: PopupSurface, positioner: PositionerState) {
|
fn new_popup(&mut self, popup: PopupSurface, positioner: PositionerState) {
|
||||||
|
self.popup_manager
|
||||||
|
.track_popup(PopupKind::Xdg(popup.clone()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let id = rand::thread_rng().gen_range(0..u64::MAX);
|
let id = rand::thread_rng().gen_range(0..u64::MAX);
|
||||||
popup.wl_surface().insert_data(SurfaceId::Child(id));
|
popup.wl_surface().insert_data(SurfaceId::Child(id));
|
||||||
let Some(parent) = popup.get_parent_surface() else {
|
let Some(parent) = popup.get_parent_surface() else {
|
||||||
|
warn!("No parent surface found for popup");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let _ = popup.send_configure();
|
|
||||||
CoreSurface::add_to(popup.wl_surface());
|
CoreSurface::add_to(popup.wl_surface());
|
||||||
|
|
||||||
popup.wl_surface().insert_data(Mutex::new(ChildInfo {
|
popup.wl_surface().insert_data(Mutex::new(ChildInfo {
|
||||||
@@ -241,6 +247,7 @@ impl XdgShellHandler for WaylandState {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let Some(panel_item) = surface_panel_item(&parent) else {
|
let Some(panel_item) = surface_panel_item(&parent) else {
|
||||||
|
warn!("No panel item found for popup parent");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let panel_item_weak = Arc::downgrade(&panel_item);
|
let panel_item_weak = Arc::downgrade(&panel_item);
|
||||||
@@ -252,6 +259,7 @@ impl XdgShellHandler for WaylandState {
|
|||||||
}
|
}
|
||||||
surf.insert_data(panel_item_weak.clone());
|
surf.insert_data(panel_item_weak.clone());
|
||||||
let Some(panel) = surface_panel_item(surf) else {
|
let Some(panel) = surface_panel_item(surf) else {
|
||||||
|
warn!("Failed to get panel item for popup surface");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
panel.backend.new_child(surf);
|
panel.backend.new_child(surf);
|
||||||
@@ -464,6 +472,7 @@ impl Backend for XdgBackend {
|
|||||||
|
|
||||||
fn close_toplevel(&self) {
|
fn close_toplevel(&self) {
|
||||||
if let Some(toplevel) = self.toplevel.lock().clone() {
|
if let Some(toplevel) = self.toplevel.lock().clone() {
|
||||||
|
self.seat.unfocus_internal_state(toplevel.wl_surface());
|
||||||
toplevel.send_close();
|
toplevel.send_close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,11 +524,11 @@ impl Backend for XdgBackend {
|
|||||||
self.seat.pointer_scroll(scroll_distance, scroll_steps)
|
self.seat.pointer_scroll(scroll_distance, scroll_steps)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyboard_keys(&self, surface: &SurfaceId, keymap_id: u64, keys: Vec<i32>) {
|
fn keyboard_key(&self, surface: &SurfaceId, keymap_id: u64, key: u32, pressed: bool) {
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {
|
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.seat.keyboard_keys(surface, keymap_id, keys)
|
self.seat.keyboard_key(surface, keymap_id, key, pressed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>) {
|
fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user