Compare commits
85 Commits
0.45.0
...
Nervyalloy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08c7a5efea | ||
|
|
b5dcffd7c0 | ||
|
|
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 | ||
|
|
3e31905b5b | ||
|
|
c830becbff | ||
|
|
e8e485b472 | ||
|
|
bf68b87813 | ||
|
|
9546a36200 |
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@@ -3,7 +3,7 @@ name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
build_and_package:
|
||||
@@ -11,13 +11,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- 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
|
||||
|
||||
- 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
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -26,21 +26,3 @@ jobs:
|
||||
|
||||
- name: Build server
|
||||
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'
|
||||
375
Cargo.lock
generated
375
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@@ -50,7 +50,7 @@ dependencies = [
|
||||
"ndk-context",
|
||||
"ndk-sys",
|
||||
"num_enum 0.7.3",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -87,16 +87,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
@@ -158,15 +148,6 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "approx"
|
||||
version = "0.4.0"
|
||||
@@ -217,7 +198,7 @@ checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"slab",
|
||||
]
|
||||
@@ -291,7 +272,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -331,7 +312,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -348,7 +329,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -470,15 +451,6 @@ version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "blocking"
|
||||
version = "1.6.1"
|
||||
@@ -509,7 +481,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -578,7 +550,7 @@ version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
||||
dependencies = [
|
||||
"approx 0.4.0",
|
||||
"approx",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
@@ -613,7 +585,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -648,10 +620,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"color-spantrace",
|
||||
"eyre",
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"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]]
|
||||
@@ -727,15 +713,6 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
@@ -766,16 +743,6 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cty"
|
||||
version = "0.2.2"
|
||||
@@ -789,13 +756,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
name = "dashmap"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -866,22 +837,23 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "drm"
|
||||
version = "0.12.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1"
|
||||
checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bytemuck",
|
||||
"drm-ffi",
|
||||
"drm-fourcc",
|
||||
"libc",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "drm-ffi"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53"
|
||||
checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b"
|
||||
dependencies = [
|
||||
"drm-sys",
|
||||
"rustix",
|
||||
@@ -895,9 +867,9 @@ checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
||||
|
||||
[[package]]
|
||||
name = "drm-sys"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986"
|
||||
checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.6.5",
|
||||
@@ -942,7 +914,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1002,15 +974,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
@@ -1085,11 +1048,11 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.3.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
|
||||
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||
dependencies = [
|
||||
"fastrand 2.1.1",
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"parking",
|
||||
@@ -1104,7 +1067,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1126,11 +1089,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@@ -1149,16 +1109,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
@@ -1189,9 +1139,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.28.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
|
||||
checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a"
|
||||
dependencies = [
|
||||
"mint",
|
||||
"serde",
|
||||
@@ -1475,7 +1425,7 @@ dependencies = [
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
"walkdir",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
@@ -1503,7 +1453,7 @@ checksum = "062c875482ccb676fd40c804a40e3824d4464c18c364547456d1c8e8e951ae47"
|
||||
dependencies = [
|
||||
"miette",
|
||||
"nom",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1624,7 +1574,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex-syntax 0.8.4",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1658,7 +1608,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1708,7 +1658,7 @@ checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
|
||||
dependencies = [
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
@@ -1720,7 +1670,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1797,7 +1747,7 @@ dependencies = [
|
||||
"raw-window-handle 0.4.3",
|
||||
"raw-window-handle 0.5.2",
|
||||
"raw-window-handle 0.6.2",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1926,7 +1876,7 @@ dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2091,7 +2041,7 @@ dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
@@ -2137,7 +2087,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2159,7 +2109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand",
|
||||
"futures-io",
|
||||
]
|
||||
|
||||
@@ -2184,12 +2134,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
@@ -2205,17 +2149,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
@@ -2260,7 +2193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2283,7 +2216,7 @@ dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2343,15 +2276,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.4.3"
|
||||
@@ -2399,7 +2323,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2501,12 +2425,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scan_fmt"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248"
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
@@ -2548,7 +2466,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2571,7 +2489,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2583,17 +2501,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@@ -2663,8 +2570,8 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/smithay/smithay.git#656178be0a19ae4c577c9c93a3d4ebfdb80e649c"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/smithay/smithay.git#0c2230f858580b52d628087d6dae1795278b8756"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags 2.6.0",
|
||||
@@ -2678,17 +2585,14 @@ dependencies = [
|
||||
"errno",
|
||||
"gl_generator",
|
||||
"indexmap 2.5.0",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libloading",
|
||||
"once_cell",
|
||||
"profiling",
|
||||
"rand",
|
||||
"rustix",
|
||||
"scan_fmt",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
"tracing",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-misc",
|
||||
@@ -2716,9 +2620,10 @@ checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d"
|
||||
[[package]]
|
||||
name = "stardust-xr"
|
||||
version = "0.45.0"
|
||||
source = "git+https://github.com/StardustXR/core.git?branch=dev#c5591c713e85cc2df9c63583fb3026e6c93ccef7"
|
||||
source = "git+https://github.com/StardustXR/core.git?branch=dev#d2964d8db079afaadb7faa4987e34814e62d6279"
|
||||
dependencies = [
|
||||
"cluFlock",
|
||||
"color-eyre",
|
||||
"dirs",
|
||||
"global_counter",
|
||||
"mint",
|
||||
@@ -2728,7 +2633,7 @@ dependencies = [
|
||||
"serde",
|
||||
"shiva-color-rs",
|
||||
"stardust-xr-schemas",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -2736,7 +2641,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "stardust-xr-schemas"
|
||||
version = "1.5.3"
|
||||
source = "git+https://github.com/StardustXR/core.git?branch=dev#c5591c713e85cc2df9c63583fb3026e6c93ccef7"
|
||||
source = "git+https://github.com/StardustXR/core.git?branch=dev#d2964d8db079afaadb7faa4987e34814e62d6279"
|
||||
dependencies = [
|
||||
"flatbuffers",
|
||||
"flexbuffers",
|
||||
@@ -2744,10 +2649,10 @@ dependencies = [
|
||||
"futures-util",
|
||||
"kdl",
|
||||
"manifest-dir-macros",
|
||||
"random-string",
|
||||
"nanoid",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
"tokio",
|
||||
"zbus",
|
||||
]
|
||||
@@ -2759,19 +2664,15 @@ dependencies = [
|
||||
"clap",
|
||||
"color-eyre",
|
||||
"console-subscriber",
|
||||
"dashmap",
|
||||
"directories",
|
||||
"glam",
|
||||
"global_counter",
|
||||
"input-event-codes",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mint",
|
||||
"nanoid",
|
||||
"nix 0.29.0",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.3",
|
||||
"portable-atomic",
|
||||
"prisma",
|
||||
"rand",
|
||||
"rustc-hash",
|
||||
"send_wrapper",
|
||||
@@ -2782,6 +2683,7 @@ dependencies = [
|
||||
"stardust-xr",
|
||||
"stardust-xr-server-codegen",
|
||||
"stereokit-rust",
|
||||
"thiserror 2.0.9",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
@@ -2802,7 +2704,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"split-iter",
|
||||
"stardust-xr-schemas",
|
||||
"stardust-xr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2814,12 +2716,12 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
[[package]]
|
||||
name = "stereokit-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/technobaboo/StereoKit-rust.git#93829181d5eea954e15dba9d9bfb0ec62e98d400"
|
||||
source = "git+https://github.com/mvvvv/StereoKit-rust.git?rev=73ffaae6f42aa369e599a6ea0391f77840d682d8#73ffaae6f42aa369e599a6ea0391f77840d682d8"
|
||||
|
||||
[[package]]
|
||||
name = "stereokit-rust"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/technobaboo/StereoKit-rust.git#93829181d5eea954e15dba9d9bfb0ec62e98d400"
|
||||
source = "git+https://github.com/mvvvv/StereoKit-rust.git?rev=73ffaae6f42aa369e599a6ea0391f77840d682d8#73ffaae6f42aa369e599a6ea0391f77840d682d8"
|
||||
dependencies = [
|
||||
"android-activity",
|
||||
"android_logger",
|
||||
@@ -2833,7 +2735,7 @@ dependencies = [
|
||||
"ndk-sys",
|
||||
"openxr-sys",
|
||||
"stereokit-macros",
|
||||
"thiserror",
|
||||
"thiserror 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2871,7 +2773,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2887,9 +2789,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.77"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2915,7 +2817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -2938,7 +2840,16 @@ version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
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]]
|
||||
@@ -2949,7 +2860,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"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]]
|
||||
@@ -2998,7 +2920,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3151,7 +3073,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3164,6 +3086,16 @@ dependencies = [
|
||||
"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]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
@@ -3230,12 +3162,6 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "uds_windows"
|
||||
version = "1.1.0"
|
||||
@@ -3339,9 +3265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.4"
|
||||
version = "0.32.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0"
|
||||
checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"wayland-backend",
|
||||
@@ -3388,9 +3314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-server"
|
||||
version = "0.31.5"
|
||||
version = "0.31.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f18d47038c0b10479e695d99ed073e400ccd9bdbb60e6e503c96f62adcb12b6"
|
||||
checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"downcast-rs",
|
||||
@@ -3433,7 +3359,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3473,7 +3399,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3484,7 +3410,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3738,6 +3664,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg-home"
|
||||
version = "1.3.0"
|
||||
@@ -3776,7 +3711,7 @@ dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"thiserror 1.0.63",
|
||||
"unicase",
|
||||
"xkbcommon-rs-codegen",
|
||||
"xkeysym",
|
||||
@@ -3809,9 +3744,9 @@ checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601"
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "4.4.0"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
|
||||
checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
@@ -3826,20 +3761,18 @@ dependencies = [
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"futures-lite",
|
||||
"hex",
|
||||
"nix 0.29.0",
|
||||
"ordered-stream",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"sha1",
|
||||
"static_assertions",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
"winnow 0.7.4",
|
||||
"xdg-home",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
@@ -3848,22 +3781,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "4.4.0"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
|
||||
checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "3.0.0"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
|
||||
checksum = "cdc27fbd3593ff015cef906527a2ec4115e2e3dbf6204a24d952ac4975c80614"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
@@ -3888,42 +3823,46 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "4.2.0"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
|
||||
checksum = "c690a1da8858fd4377b8cc3134a753b0bea1d8ebd78ad6e5897fab821c5e184e"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant_derive",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "4.2.0"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
|
||||
checksum = "83b6ddc1fed08493e4f2bd9350e7d00a3383467228735f3f169a9f8820fde755"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_utils"
|
||||
version = "2.1.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
|
||||
checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"syn 2.0.87",
|
||||
"winnow 0.7.4",
|
||||
]
|
||||
|
||||
35
Cargo.toml
35
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.75"
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
name = "stardust-xr-server"
|
||||
version = "0.45.0"
|
||||
authors = ["Nova King <technobaboo@proton.me>"]
|
||||
@@ -12,6 +12,10 @@ homepage = "https://stardustxr.org"
|
||||
[workspace]
|
||||
members = ["codegen"]
|
||||
|
||||
[workspace.dependencies.stardust-xr]
|
||||
git = "https://github.com/StardustXR/core.git"
|
||||
branch = "dev"
|
||||
|
||||
[[bin]]
|
||||
name = "stardust-xr-server"
|
||||
path = "src/main.rs"
|
||||
@@ -37,25 +41,24 @@ auto_link_exclude_list = [
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
debug = true
|
||||
strip = "none"
|
||||
strip = false
|
||||
debug-assertions = true
|
||||
overflow-checks = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
debug = "line-tables-only"
|
||||
strip = "none"
|
||||
strip = true
|
||||
debug-assertions = true
|
||||
overflow-checks = false
|
||||
lto = "thin"
|
||||
|
||||
[dependencies]
|
||||
# small utility thingys
|
||||
once_cell = "1.19.0"
|
||||
nanoid = "0.4.0"
|
||||
lazy_static = "1.5.0"
|
||||
rand = "0.8.5"
|
||||
rustc-hash = "2.0.0"
|
||||
portable-atomic = { version = "1.7.0", features = ["float", "std"] }
|
||||
send_wrapper = "0.6.0"
|
||||
slotmap = "1.0.7"
|
||||
global_counter = "=0.2.2"
|
||||
@@ -65,6 +68,7 @@ parking_lot = "0.12.3"
|
||||
color-eyre = { version = "0.6.3", default-features = false }
|
||||
clap = { version = "4.5.13", features = ["derive"] }
|
||||
console-subscriber = { version = "0.4.0", optional = true }
|
||||
thiserror = "2.0.9"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
tracing-tracy = { version = "0.11.1", optional = true }
|
||||
@@ -75,43 +79,36 @@ serde_repr = "0.1.19"
|
||||
toml = "0.8.19"
|
||||
|
||||
# mathy stuffs
|
||||
glam = { version = "0.28.0", features = ["mint", "serde"] }
|
||||
glam = { version = "0.29.0", features = ["mint", "serde"] }
|
||||
mint = "0.5.9"
|
||||
tokio = { version = "1.39.2", features = ["rt-multi-thread", "signal", "time"] }
|
||||
prisma = "0.1.1"
|
||||
|
||||
# linux stuffs
|
||||
libc = "0.2.155"
|
||||
nix = "0.29.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"
|
||||
xkbcommon-rs = "0.1.0"
|
||||
|
||||
# wayland
|
||||
wayland-backend = { version = "0.3.7", optional = true, default-features = false }
|
||||
wayland-scanner = { version = "0.31.4", optional = true }
|
||||
dashmap = "6.1.0"
|
||||
|
||||
[dependencies.smithay]
|
||||
# git = "https://github.com/technobaboo/smithay.git"
|
||||
# git = "https://github.com/colinmarc/smithay.git"
|
||||
git = "https://github.com/smithay/smithay.git"
|
||||
# path = "../smithay"
|
||||
default-features = false
|
||||
features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"]
|
||||
optional = true
|
||||
|
||||
|
||||
[dependencies.stereokit-rust]
|
||||
# path = "../StereoKit-rust"
|
||||
# git = "https://github.com/mvvvv/StereoKit-rust.git"
|
||||
git = "https://github.com/technobaboo/StereoKit-rust.git"
|
||||
git = "https://github.com/mvvvv/StereoKit-rust.git"
|
||||
rev = "73ffaae6f42aa369e599a6ea0391f77840d682d8"
|
||||
features = ["no-event-loop"]
|
||||
default-features = false
|
||||
|
||||
[dependencies.stardust-xr]
|
||||
git = "https://github.com/StardustXR/core.git"
|
||||
branch = "dev"
|
||||
workspace = true
|
||||
|
||||
[dependencies.stardust-xr-server-codegen]
|
||||
path = "codegen"
|
||||
|
||||
154
README.md
154
README.md
@@ -1,85 +1,113 @@
|
||||
# Stardust XR Reference Server
|
||||
# Stardust XR Server
|
||||
|
||||
This project is a usable Linux display server that reinvents human-computer interaction for all kinds of XR, from putting 2D/XR apps into various 3D shells for varying uses to SDF-based interaction.
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
1. Cargo
|
||||
2. CMake
|
||||
3. EGL+GLES 3.2
|
||||
4. GLX+Xlib
|
||||
5. fontconfig
|
||||
6. dlopen
|
||||
7. OpenXR Loader (required even if run in flatscreen mode)
|
||||

|
||||
|
||||
Command line installation of core & dynamic dependencies are provided below:
|
||||
<details>
|
||||
<summary>Ubuntu/Debian</summary>
|
||||
<pre><code class="language-bash">
|
||||
sudo apt update && sudo apt install \
|
||||
build-essential \
|
||||
cargo \
|
||||
cmake \
|
||||
libxkbcommon-dev libudev1 libinput10 libcap2 libmtdev1 libevdev2 libwacom9 libgudev-1.0-0 \
|
||||
libglib2.0-dev libffi8 libpcre2-dev libxkbcommon-x11-dev libxcb-dev libxcb-xkb-dev libxau-dev \
|
||||
libstdc++-dev libx11-dev libxfixes-dev libegl-dev libgbm-dev libfontconfig1-dev libgl-dev \
|
||||
libdrm-dev libexpat1-dev libfreetype6-dev libxml2-dev zlib1g-dev libbz2-dev libpng-dev \
|
||||
libharfbuzz-dev libbrotli-dev liblzma-dev libraphite2-dev
|
||||
</code></pre>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Fedora</summary>
|
||||
<pre><code class="language-bash">
|
||||
sudo apt update && sudo apt install \
|
||||
cargo \
|
||||
cmake \
|
||||
libxkbcommon-devel systemd-devel libinput-devel libcap-devel mtdev-devel libevdev-devel glib2-devel \
|
||||
libffi-devel pcre2-devel libxkbcommon-x11-devel libxcb-devel libXau-devel libstdc++-devel libx11-devel libxfixes-devel \
|
||||
mesa-libEGL-devel mesa-libgbm-devel fontconfig-devel libdrm-devel expat-devel freetype-devel libxml2-devel zlib-devel \
|
||||
bzip2-devel libpng-devel harfbuzz-devel brotli-devel xz-devel graphite2-devel
|
||||
</code></pre>
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Arch Linux</summary>
|
||||
<pre><code class="language-bash">
|
||||
sudo pacman -Syu --needed \
|
||||
cargo \
|
||||
cmake \
|
||||
libxkbcommon systemd libinput libcap mtdev libevdev libwacom glib2 libffi pcre2 libxkbcommon-x11 \
|
||||
libxcb libxau libx11 libxfixes mesa fontconfig libdrm expat freetype2 libxml2 zlib bzip2 \
|
||||
libpng harfbuzz brotli xz graphite
|
||||
</code></pre>
|
||||
</details>
|
||||
|
||||
## Installation
|
||||
|
||||
More detailed instructions and walkthroughs are provided at https://www.stardustxr.org
|
||||
|
||||
The [Terra Repository](https://terra.fyralabs.com/) is required, and comes pre-installed with [Ultramarine Linux](https://ultramarine-linux.org/). Other Fedora Editions and derivatives can directly install terra-release:
|
||||
|
||||
## Build
|
||||
```bash
|
||||
cargo build
|
||||
sudo dnf install --nogpgcheck --repofrompath 'terra,https://repos.fyralabs.com/terra$releasever' terra-release
|
||||
```
|
||||
|
||||
For a full installation of the Stardust XR server *and* a selected group of clients, run:
|
||||
|
||||
```bash
|
||||
sudo dnf group install stardust-xr
|
||||
```
|
||||
|
||||
## Manual Build
|
||||
We've provided a manual installation script [here](https://github.com/cyberneticmelon/usefulscripts/blob/main/stardustxr_setup.sh) that clones and builds the Stardust XR server along with a number of other clients from their respective repositories, and provides a startup script for automatically launching some clients.
|
||||
|
||||
After cloning the repository
|
||||
```bash
|
||||
cargo build --release # this is needed to skip validation layers
|
||||
```
|
||||
The latest stable server is automatically built to an appimage at https://github.com/StardustXR/server/releases for easy testing.
|
||||
|
||||
## Usage
|
||||
> [!NOTE]
|
||||
> For help with setting up an XR headset on linux, visit https://stardustxr.org/docs/get-started/setup-openxr
|
||||
|
||||
First, try running `cargo run` in a terminal window. If a headset is plugged in and OpenXR is working no window will show up. However, the headset should show the same things as the window that opens:
|
||||
|
||||

|
||||
The **Stardust XR Server** is a server that runs clients, so without any running, you will see a black screen. If you only have the server installed, we recommend also cloning and building the following clients to start: [Flatland](https://github.com/StardustXR/flatland), which allows normal 2D apps to run in Stardust, [Protostar](https://github.com/StardustXR/protostar), which contains Hexagon Launcher, an app launcher menu, and [Black Hole](https://github.com/StardustXR/black-hole) to quickly tuck away your objects and apps (kind of like desktop peek on Windows).
|
||||
|
||||
Stardust won't do anything interesting without clients! Try some from https://github.com/StardustXR.
|
||||
First, try running `cargo run -- -f` in a terminal window to check out flatscreen mode, (or `stardust-xr-server -f` / `stardust-xr-server_dev -f` if you installed via dnf or the manual installation script, respectively, as they provide symlinks.)
|
||||
|
||||
### Default Sky
|
||||
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.
|
||||
|
||||
You can set a default skytex/skylight by putting your favorite HDRI equirectangular sky in `~/.config/stardust/skytex.hdr`. Certain clients can override this.
|
||||
> [!IMPORTANT]
|
||||
> [Flatland](https://github.com/StardustXR/flatland) must be running for 2D apps to launch.
|
||||
|
||||
Flatscreen mode when the default skybox is [Zhengyang Gate](https://polyhaven.com/a/zhengyang_gate):
|
||||

|
||||
### 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).
|
||||
|
||||
### Windowed Mode
|
||||
### Flatscreen Navigation
|
||||
A video guide showcasing flatscreen controls is available [here](https://www.youtube.com/watch?v=JCYecSlKlDI)
|
||||
|
||||
If the stardust server can't connect to an OpenXR runtime or you force it into flatscreen mode with `-f`, the server will show in a window.
|
||||

|
||||
To move around, hold down `Shift + W A S D`, with `Q` for moving down and `E` for moving up.
|
||||

|
||||
|
||||
You can navigate around by right click + dragging to look around, Shift+W/A/S/D/Q/E to move. If you have a virtual hand, left click pinches, right click points, both make a fist.
|
||||
To look around, hold down `Shift + Right` Click while moving the mouse.
|
||||

|
||||
|
||||
### Flags
|
||||
#### Flatscreen (-f)
|
||||
To drag applications out of the app launcher, hold down `Shift + ~`
|
||||

|
||||
|
||||
The server will show up in windowed mode no matter what with your mouse pointer being turned into a 3D pointer. Keyboard input will be sent to whatever your mouse is hovering over like visionOS simulator.
|
||||
Flatscreen mode upon initial startup:
|
||||

|
||||
### XR Navigation
|
||||
A video guide showcasing XR controls is available [here](https://www.youtube.com/watch?v=RbxFq6JjliA)
|
||||
|
||||
#### Overlay (-o \<PRIORITY>)
|
||||
**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
|
||||
|
||||
The server will, if in XR mode, be overlaid using the OpenXR overlay extension with the given priority.
|
||||

|
||||
|
||||
#### Disable controller (--disable-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
|
||||
|
||||
Some runtimes such as Monado may emulate a controller using a hand, and this messes with Stardust's input system. Set this flag to ignore the controllers that the OpenXR runtime provides.
|
||||
|
||||
#### Execute (-e </path/to/executable>)
|
||||
|
||||
When wayland and OpenXR and such are initialized, run the given executable (such as a bash script) with all the environment variables needed to connect all clients of any type to the server. If not set, the server will run the executable at `~/.config/stardust/startup` if it exists. This is how stardust desktop environments can be made.
|
||||
|
||||
#### Help (-h, --help)
|
||||
|
||||
help
|
||||
|
||||
## Test
|
||||
|
||||
##### Gnome Graphical Integration Test
|
||||
|
||||
- `nix build .#gnome-graphical-test`
|
||||
|
||||
This test uses Nix to reproducibly execute a QEMU virtual machine which
|
||||
spawns a full Gnome desktop. It runs `monado-service`, `stardust-xr-server`
|
||||
`flatland` underneath of Gnome and then attaches `weston-cliptest` to the
|
||||
`flatland` process running underneath of `stardust-xr-server`, the result is
|
||||
a screenshot in PNG format that should look like expected. If any process in
|
||||
this test produces an exit code above 0, the test will fail, graphical bugs
|
||||
should be visible in the screenshot. An example of the result is below.
|
||||
|
||||
###### Result
|
||||
|
||||

|
||||
|
||||
##### Everything
|
||||
|
||||
`nix flake check` will build every test underneath of the `checks` attribute in the `flake.nix`
|
||||

|
||||
|
||||
@@ -13,6 +13,4 @@ mint = "0.5.9"
|
||||
proc-macro2 = "1.0.71"
|
||||
split-iter = "0.1.0"
|
||||
|
||||
[dependencies.stardust-xr-schemas]
|
||||
git = "https://github.com/StardustXR/core.git"
|
||||
branch = "dev"
|
||||
stardust-xr = { workspace = true }
|
||||
|
||||
@@ -2,7 +2,7 @@ use convert_case::{Case, Casing};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use split_iter::Splittable;
|
||||
use stardust_xr_schemas::protocol::*;
|
||||
use stardust_xr::schemas::protocol::*;
|
||||
|
||||
fn fold_tokens(a: TokenStream, b: TokenStream) -> TokenStream {
|
||||
quote!(#a #b)
|
||||
@@ -25,10 +25,6 @@ pub fn codegen_field_protocol(_input: proc_macro::TokenStream) -> proc_macro::To
|
||||
codegen_protocol(FIELD_PROTOCOL)
|
||||
}
|
||||
#[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 {
|
||||
codegen_protocol(AUDIO_PROTOCOL)
|
||||
}
|
||||
@@ -64,11 +60,28 @@ fn codegen_protocol(protocol: &'static str) -> proc_macro::TokenStream {
|
||||
};
|
||||
let aspect = generate_aspect(&Aspect {
|
||||
name: "interface".to_string(),
|
||||
id: 0,
|
||||
description: protocol.description.clone(),
|
||||
inherits: vec![],
|
||||
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();
|
||||
let custom_enums = protocol
|
||||
@@ -130,7 +143,7 @@ fn generate_custom_union(custom_union: &CustomUnion) -> TokenStream {
|
||||
quote! {
|
||||
#[doc = #description]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
pub enum #name {#option_decls}
|
||||
}
|
||||
}
|
||||
@@ -177,11 +190,12 @@ fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
||||
Span::call_site(),
|
||||
);
|
||||
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)
|
||||
.map(|t| {
|
||||
// TODO: properly import all dependencies
|
||||
quote! {
|
||||
#[allow(clippy::all)]
|
||||
pub mod #client_mod_name {
|
||||
use super::*;
|
||||
#t
|
||||
@@ -190,11 +204,6 @@ fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let aspect_trait_name = Ident::new(
|
||||
&format!("{}Aspect", &aspect.name.to_case(Case::Pascal)),
|
||||
Span::call_site(),
|
||||
);
|
||||
|
||||
let opcodes = aspect
|
||||
.members
|
||||
.iter()
|
||||
@@ -219,31 +228,95 @@ fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
||||
let alias_info = generate_alias_info(aspect);
|
||||
|
||||
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)
|
||||
.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
|
||||
.iter()
|
||||
.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)
|
||||
.map(|members| {
|
||||
quote! {
|
||||
fn add_node_members(node: &crate::nodes::Node) {
|
||||
#members
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let server_side_members = quote! {
|
||||
#[allow(clippy::all)]
|
||||
#[doc = #description]
|
||||
pub trait #aspect_trait_name {
|
||||
#add_node_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 {
|
||||
@@ -283,6 +356,7 @@ fn generate_alias_info(aspect: &Aspect) -> TokenStream {
|
||||
|
||||
quote! {
|
||||
lazy_static::lazy_static! {
|
||||
#[allow(clippy::all)]
|
||||
pub static ref #aspect_alias_info_name: crate::nodes::alias::AliasInfo = crate::nodes::alias::AliasInfo {
|
||||
server_signals: vec![#local_signals],
|
||||
server_methods: vec![#local_methods],
|
||||
@@ -293,8 +367,8 @@ fn generate_alias_info(aspect: &Aspect) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_member(member: &Member) -> TokenStream {
|
||||
let id = member.opcode;
|
||||
fn generate_member(aspect_id: u64, aspect_name: &str, member: &Member) -> TokenStream {
|
||||
let opcode = member.opcode;
|
||||
let name = Ident::new(&member.name.to_case(Case::Snake), Span::call_site());
|
||||
let description = &member.description;
|
||||
|
||||
@@ -324,40 +398,53 @@ fn generate_member(member: &Member) -> TokenStream {
|
||||
.as_ref()
|
||||
.map(|r| generate_argument_type(r, false, true))
|
||||
.unwrap_or_else(|| quote!(()));
|
||||
let name_str = name.to_string();
|
||||
|
||||
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) => {
|
||||
quote! {
|
||||
#[doc = #description]
|
||||
pub fn #name(#argument_decls) -> color_eyre::eyre::Result<()> {
|
||||
let serialized = stardust_xr::schemas::flex::serialize((#argument_uses))?;
|
||||
_node.send_remote_signal(#id, serialized)
|
||||
pub fn #name(#argument_decls) -> crate::core::error::Result<()> {
|
||||
|
||||
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! {
|
||||
#[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) => {
|
||||
quote! {
|
||||
#[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 member_name_ident = Ident::new(&member.name, Span::call_site());
|
||||
|
||||
@@ -379,7 +466,10 @@ fn generate_handler(member: &Member) -> TokenStream {
|
||||
.clone()
|
||||
.zip(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();
|
||||
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))
|
||||
.reduce(|a, b| quote!(#a, #b))
|
||||
.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! {
|
||||
node.add_local_signal(#opcode, |_node, _calling_client, _message| {
|
||||
#opcode => { let result = (move || {
|
||||
#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! {
|
||||
node.add_local_method(#opcode, |_node, _calling_client, _message, _method_response| {
|
||||
_method_response.wrap_async(async move {
|
||||
#opcode => _method_response.wrap_async(async move {
|
||||
#deserialize
|
||||
let result = Self::#member_name_ident(_node, _calling_client.clone(), #argument_uses).await?;
|
||||
Ok((#serialize, Vec::new()))
|
||||
});
|
||||
});
|
||||
let result = <Self as #aspect_name>::#member_name_ident(_node, _calling_client.clone(), #argument_uses).await;
|
||||
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 {
|
||||
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 {
|
||||
ArgumentType::Color => quote!(color::rgba_linear!(#name[0], #name[1], #name[2], #name[3])),
|
||||
ArgumentType::Vec(v) => {
|
||||
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) => {
|
||||
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),
|
||||
}
|
||||
@@ -474,11 +577,11 @@ fn generate_argument_serialize(
|
||||
ArgumentType::Color => quote!([#name.c.r, #name.c.g, #name.c.b, #name.a]),
|
||||
ArgumentType::Vec(v) => {
|
||||
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) => {
|
||||
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),
|
||||
}
|
||||
@@ -511,6 +614,7 @@ fn argument_type_option_name(argument_type: &ArgumentType) -> String {
|
||||
ArgumentType::Union(u) => u.clone(),
|
||||
ArgumentType::Struct(s) => s.clone(),
|
||||
ArgumentType::Node { _type, .. } => _type.clone(),
|
||||
ArgumentType::Fd => "File Descriptor".to_string(),
|
||||
}
|
||||
}
|
||||
fn generate_argument_type(
|
||||
@@ -607,6 +711,9 @@ fn generate_argument_type(
|
||||
quote!(std::sync::Arc<crate::nodes::Node>)
|
||||
}
|
||||
}
|
||||
ArgumentType::Fd => {
|
||||
quote!(&std::os::fd::OwnedFd)
|
||||
}
|
||||
};
|
||||
|
||||
if optional {
|
||||
|
||||
51
flake.lock
generated
51
flake.lock
generated
@@ -26,11 +26,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722555600,
|
||||
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
|
||||
"lastModified": 1743550720,
|
||||
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
|
||||
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -47,11 +47,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712014858,
|
||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||
"lastModified": 1743550720,
|
||||
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -65,11 +65,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1713050562,
|
||||
"narHash": "sha256-m7c6XpmpTM1URuqMG2KqtaWbL2Vt8vJFJtmvq123BmY=",
|
||||
"lastModified": 1725868201,
|
||||
"narHash": "sha256-rDBQ9tXQCCA7emikSYH59ADJELE2IpzB7eoLrpHYzU4=",
|
||||
"owner": "StardustXR",
|
||||
"repo": "flatland",
|
||||
"rev": "b3b0f29c4ea1b82c96cf9de507837bf15a5e4c0e",
|
||||
"rev": "0914dd3df54a5e6258dfc0a02d65af1c0fc0fc90",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -84,11 +84,11 @@
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719226092,
|
||||
"narHash": "sha256-YNkUMcCUCpnULp40g+svYsaH1RbSEj6s4WdZY/SHe38=",
|
||||
"lastModified": 1747284884,
|
||||
"narHash": "sha256-lTSKhRrassMcJ1ZsuUVunyl/F04vvCKY80HB/4rvvm4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "hercules-ci-effects",
|
||||
"rev": "11e4b8dc112e2f485d7c97e1cee77f9958f498f5",
|
||||
"rev": "7168f6002a6b48a9b6151e1e97e974a0722ecfdc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -115,23 +115,26 @@
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1722555339,
|
||||
"narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
|
||||
"lastModified": 1743296961,
|
||||
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1713714899,
|
||||
"narHash": "sha256-+z/XjO3QJs5rLE5UOf015gdVauVRQd2vZtsFkaXBq2Y=",
|
||||
"lastModified": 1747179050,
|
||||
"narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6143fc5eeb9c4f00163267708e26191d1e918932",
|
||||
"rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -143,11 +146,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1723991338,
|
||||
"narHash": "sha256-Grh5PF0+gootJfOJFenTTxDTYPidA3V28dqJ/WV7iis=",
|
||||
"lastModified": 1747542820,
|
||||
"narHash": "sha256-GaOZntlJ6gPPbbkTLjbd8BMWaDYafhuuYRNrxCGnPJw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8a3354191c0d7144db9756a74755672387b702ba",
|
||||
"rev": "292fa7d4f6519c074f0a50394dbbe69859bb6043",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
135
flake.nix
135
flake.nix
@@ -15,13 +15,21 @@
|
||||
flatland.url = "github:StardustXR/flatland";
|
||||
};
|
||||
outputs =
|
||||
inputs@{ self, flake-parts, nixpkgs, hercules-ci-effects, flatland, ... }:
|
||||
inputs@{
|
||||
self,
|
||||
flake-parts,
|
||||
nixpkgs,
|
||||
hercules-ci-effects,
|
||||
flatland,
|
||||
...
|
||||
}:
|
||||
let
|
||||
name = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
|
||||
src = builtins.path {
|
||||
name = "${name}-source";
|
||||
path = toString ./.;
|
||||
filter = path: type:
|
||||
filter =
|
||||
path: type:
|
||||
nixpkgs.lib.all (n: builtins.baseNameOf path != n) [
|
||||
"flake.nix"
|
||||
"flake.lock"
|
||||
@@ -29,63 +37,84 @@
|
||||
"README.md"
|
||||
];
|
||||
};
|
||||
in flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
in
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [ flake-parts.flakeModules.easyOverlay ];
|
||||
systems = [ "aarch64-linux" "x86_64-linux" "riscv64-linux" ];
|
||||
perSystem = { config, self', inputs', pkgs, system, ... }: {
|
||||
_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;
|
||||
systems = [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
"riscv64-linux"
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
config,
|
||||
self',
|
||||
inputs',
|
||||
pkgs,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
{
|
||||
_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 = {
|
||||
herculesCI.ciSystems = [ "x86_64-linux" ];
|
||||
effects = let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
||||
in { ref, rev, ... }: {
|
||||
gnome-graphical-test = hci-effects.mkEffect {
|
||||
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
||||
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
||||
effectScript = ''
|
||||
readSecretString stardustxrDiscord .webhook > .webhook
|
||||
readSecretString stardustxrIpfs .basicauth > .basicauth
|
||||
set -x
|
||||
export RESPONSE=$(curl -H @.basicauth -F file=@${
|
||||
self.packages."x86_64-linux".gnome-graphical-test
|
||||
}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
|
||||
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
|
||||
set +x
|
||||
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
||||
${pkgs.discord-sh}/bin/discord.sh \
|
||||
--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 "Ref;${ref}" \
|
||||
--field "Commit ID;${rev}" \
|
||||
--field "Flatland Revision;${flatland.rev}" \
|
||||
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
||||
--image "$ADDRESS"
|
||||
'';
|
||||
effects =
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
||||
in
|
||||
{ ref, rev, ... }:
|
||||
{
|
||||
gnome-graphical-test = hci-effects.mkEffect {
|
||||
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
||||
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
||||
effectScript = ''
|
||||
readSecretString stardustxrDiscord .webhook > .webhook
|
||||
readSecretString stardustxrIpfs .basicauth > .basicauth
|
||||
set -x
|
||||
export RESPONSE=$(curl -H @.basicauth -F file=@${
|
||||
self.packages."x86_64-linux".gnome-graphical-test
|
||||
}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
|
||||
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
|
||||
set +x
|
||||
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
||||
${pkgs.discord-sh}/bin/discord.sh \
|
||||
--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 "Ref;${ref}" \
|
||||
--field "Commit ID;${rev}" \
|
||||
--field "Flatland Revision;${flatland.rev}" \
|
||||
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
||||
--image "$ADDRESS"
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 789 KiB |
BIN
img/workflow.png
Normal file
BIN
img/workflow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 872 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
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
|
||||
@@ -3,8 +3,8 @@
|
||||
let
|
||||
sk_gpu_zip = fetchurl {
|
||||
url =
|
||||
"https://github.com/StereoKit/sk_gpu/releases/download/v2024.8.16/sk_gpu.v2024.8.16.zip";
|
||||
sha256 = "sha256-Wk3PZFlWqhrsQ8xG0sQaV2xSasdg2D7TMiPvl/CgtGU=";
|
||||
"https://github.com/StereoKit/sk_gpu/releases/download/v2024.9.26/sk_gpu.v2024.9.26.zip";
|
||||
sha256 = "sha256-W32RveeCszioWGtbCsvAqB28YHvOsw2xJ15MosYLFXk=";
|
||||
};
|
||||
in stdenv.mkDerivation rec {
|
||||
name = "sk_gpu";
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
, xorg
|
||||
, fontconfig
|
||||
, libxkbcommon
|
||||
, xkeyboard_config
|
||||
, libclang
|
||||
|
||||
, cmake
|
||||
@@ -15,11 +16,13 @@
|
||||
, fetchFromGitHub
|
||||
, sk_gpu
|
||||
, libXau
|
||||
, libgbm
|
||||
|
||||
, libXdmcp
|
||||
, stdenv
|
||||
, lib
|
||||
, openxr-loader
|
||||
, makeWrapper
|
||||
}:
|
||||
|
||||
rustPlatform.buildRustPackage rec {
|
||||
@@ -47,10 +50,17 @@ rustPlatform.buildRustPackage rec {
|
||||
rev = "900e40fb5d2502927360fe2f31762bdbb624455f";
|
||||
sha256 = "sha256-zBRAXgG5Fi6+5uPQCI/RCGatY6O4ELuYBoKrPNn4K+8=";
|
||||
};
|
||||
openxr_loader = fetchFromGitHub {
|
||||
owner = "KhronosGroup";
|
||||
repo = "OpenXR-SDK";
|
||||
rev = "288d3a7ebc1ad959f62d51da75baa3d27438c499";
|
||||
sha256 = "sha256-RdmnBe26hqPmqwCHIJolF6bSmZRmIKVlGF+TXAY35ig=";
|
||||
};
|
||||
|
||||
DEP_MESHOPTIMIZER_SOURCE = "${meshoptimizer}";
|
||||
DEP_BASIS_UNIVERSAL_SOURCE = "${basis_universal}";
|
||||
DEP_SK_GPU_SOURCE = "${sk_gpu}";
|
||||
DEP_OPENXR_LOADER_SOURCE = "${openxr_loader}";
|
||||
|
||||
postPatch = let libPath = lib.makeLibraryPath [ stdenv.cc.cc.lib ];
|
||||
in ''
|
||||
@@ -69,7 +79,8 @@ rustPlatform.buildRustPackage rec {
|
||||
--set-rpath "${libPath}" \
|
||||
$sk/sk_gpu/tools/linux_x64/skshaderc
|
||||
'';
|
||||
nativeBuildInputs = [ cmake pkg-config llvmPackages.libcxxClang ];
|
||||
|
||||
nativeBuildInputs = [ cmake pkg-config llvmPackages.libcxxClang makeWrapper ];
|
||||
buildInputs = [
|
||||
libGL
|
||||
mesa
|
||||
@@ -78,9 +89,16 @@ rustPlatform.buildRustPackage rec {
|
||||
xorg.libXfixes
|
||||
fontconfig
|
||||
libxkbcommon
|
||||
xkeyboard_config
|
||||
libXau
|
||||
libXdmcp
|
||||
openxr-loader
|
||||
libgbm
|
||||
];
|
||||
LIBCLANG_PATH = "${libclang.lib}/lib";
|
||||
|
||||
postFixup = ''
|
||||
wrapProgram $out/bin/stardust-xr-server \
|
||||
--set XKB_CONFIG_ROOT "${xkeyboard_config}/share/X11/xkb"
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,46 +1,57 @@
|
||||
use super::{
|
||||
client_state::{ClientStateParsed, CLIENT_STATES},
|
||||
client_state::{CLIENT_STATES, ClientStateParsed},
|
||||
destroy_queue,
|
||||
scenegraph::Scenegraph,
|
||||
};
|
||||
use crate::{
|
||||
core::{registry::OwnedRegistry, task},
|
||||
nodes::{
|
||||
audio, data, drawable, fields, input, items,
|
||||
Node, audio, drawable, fields, input, items,
|
||||
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 lazy_static::lazy_static;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stardust_xr::messenger::{self, MessageSenderHandle};
|
||||
use std::{fmt::Debug, fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
||||
use tokio::{net::UnixStream, task::JoinHandle};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fs,
|
||||
iter::FromIterator,
|
||||
path::PathBuf,
|
||||
sync::{Arc, OnceLock},
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::{net::UnixStream, sync::watch, task::JoinHandle};
|
||||
use tracing::info;
|
||||
|
||||
lazy_static! {
|
||||
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 {
|
||||
pid: None,
|
||||
// env: None,
|
||||
exe: None,
|
||||
|
||||
dispatch_join_handle: OnceCell::new(),
|
||||
flush_join_handle: OnceCell::new(),
|
||||
disconnect_status: OnceCell::new(),
|
||||
dispatch_join_handle: OnceLock::new(),
|
||||
flush_join_handle: OnceLock::new(),
|
||||
disconnect_status: OnceLock::new(),
|
||||
|
||||
message_sender_handle: None,
|
||||
id_counter: CounterU32::new(0),
|
||||
message_last_received: INTERNAL_CLIENT_MESSAGE_TIMES.1.clone(),
|
||||
message_sender_handle: None,
|
||||
scenegraph: Default::default(),
|
||||
root: OnceCell::new(),
|
||||
root: OnceLock::new(),
|
||||
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> {
|
||||
let env = fs::read_to_string(format!("/proc/{pid}/environ"))?;
|
||||
@@ -59,16 +70,17 @@ pub struct Client {
|
||||
pub pid: Option<i32>,
|
||||
// env: Option<FxHashMap<String, String>>,
|
||||
exe: Option<PathBuf>,
|
||||
dispatch_join_handle: OnceCell<JoinHandle<Result<()>>>,
|
||||
flush_join_handle: OnceCell<JoinHandle<Result<()>>>,
|
||||
disconnect_status: OnceCell<Result<()>>,
|
||||
dispatch_join_handle: OnceLock<JoinHandle<Result<()>>>,
|
||||
flush_join_handle: OnceLock<JoinHandle<Result<()>>>,
|
||||
disconnect_status: OnceLock<Result<()>>,
|
||||
|
||||
id_counter: CounterU32,
|
||||
message_last_received: watch::Receiver<Instant>,
|
||||
pub message_sender_handle: Option<MessageSenderHandle>,
|
||||
pub scenegraph: Arc<Scenegraph>,
|
||||
pub root: OnceCell<Arc<Root>>,
|
||||
pub root: OnceLock<Arc<Root>>,
|
||||
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
|
||||
pub state: OnceCell<ClientState>,
|
||||
pub state: OnceLock<ClientState>,
|
||||
}
|
||||
impl Client {
|
||||
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
|
||||
@@ -90,21 +102,23 @@ impl Client {
|
||||
.and_then(state)
|
||||
.unwrap_or_else(|| Arc::new(ClientStateParsed::default()));
|
||||
|
||||
let (message_time_tx, message_last_received) = watch::channel(Instant::now());
|
||||
let client = CLIENTS.add(Client {
|
||||
pid,
|
||||
// env,
|
||||
exe: exe.clone(),
|
||||
|
||||
dispatch_join_handle: OnceCell::new(),
|
||||
flush_join_handle: OnceCell::new(),
|
||||
disconnect_status: OnceCell::new(),
|
||||
dispatch_join_handle: OnceLock::new(),
|
||||
flush_join_handle: OnceLock::new(),
|
||||
disconnect_status: OnceLock::new(),
|
||||
|
||||
id_counter: CounterU32::new(256),
|
||||
message_last_received,
|
||||
message_sender_handle: Some(messenger_tx.handle()),
|
||||
scenegraph: scenegraph.clone(),
|
||||
root: OnceCell::new(),
|
||||
root: OnceLock::new(),
|
||||
base_resource_prefixes: Default::default(),
|
||||
state: OnceCell::default(),
|
||||
state: OnceLock::default(),
|
||||
});
|
||||
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
|
||||
let _ = client.root.set(Root::create(&client, state.root)?);
|
||||
@@ -112,7 +126,6 @@ impl Client {
|
||||
fields::create_interface(&client)?;
|
||||
drawable::create_interface(&client)?;
|
||||
audio::create_interface(&client)?;
|
||||
data::create_interface(&client)?;
|
||||
input::create_interface(&client)?;
|
||||
items::camera::create_interface(&client)?;
|
||||
items::panel::create_interface(&client)?;
|
||||
@@ -129,7 +142,7 @@ impl Client {
|
||||
.map(|exe| exe.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(
|
||||
|| {
|
||||
format!(
|
||||
@@ -144,12 +157,14 @@ impl Client {
|
||||
if let Err(e) = messenger_rx.dispatch(&*scenegraph).await {
|
||||
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(
|
||||
|| format!("client flush pid={} exe={}", &pid_printable, &exe_printable,),
|
||||
{
|
||||
@@ -163,6 +178,7 @@ impl Client {
|
||||
}
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
Ok(client)
|
||||
@@ -200,6 +216,11 @@ impl Client {
|
||||
.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<()>) {
|
||||
let _ = self.disconnect_status.set(reason);
|
||||
if let Some(dispatch_join_handle) = self.dispatch_join_handle.get() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::client::{get_env, Client};
|
||||
use crate::nodes::{root::ClientState, spatial::Spatial, Node};
|
||||
use super::client::{Client, get_env};
|
||||
use crate::nodes::{Node, root::ClientState, spatial::Spatial};
|
||||
use glam::Mat4;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -69,7 +69,7 @@ impl ClientStateParsed {
|
||||
let app_name = self
|
||||
.launch_info
|
||||
.as_ref()
|
||||
.map(|l| l.cmdline.first().unwrap().split('/').last().unwrap())
|
||||
.map(|l| l.cmdline.first().unwrap().split('/').next_back().unwrap())
|
||||
.unwrap_or("unknown");
|
||||
let state_file_path = directory
|
||||
.join(format!("{app_name}-{}", nanoid::nanoid!()))
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use std::any::Any;
|
||||
use std::{any::Any, sync::LazyLock};
|
||||
use tokio::sync::mpsc::{self, unbounded_channel};
|
||||
|
||||
type Anything = Box<dyn Any + Send + Sync>;
|
||||
|
||||
static MAIN_DESTROY_QUEUE: Lazy<(
|
||||
static MAIN_DESTROY_QUEUE: LazyLock<(
|
||||
mpsc::UnboundedSender<Anything>,
|
||||
Mutex<mpsc::UnboundedReceiver<Anything>>,
|
||||
)> = Lazy::new(|| {
|
||||
)> = LazyLock::new(|| {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
(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 delta;
|
||||
pub mod destroy_queue;
|
||||
pub mod idl_utils;
|
||||
pub mod error;
|
||||
pub mod registry;
|
||||
pub mod resource;
|
||||
pub mod scenegraph;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
#![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 std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{Arc, LazyLock, Weak};
|
||||
|
||||
#[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> {
|
||||
pub const fn new() -> Self {
|
||||
Registry(const_mutex(None))
|
||||
}
|
||||
fn lock(&self) -> MappedMutexGuard<FxHashMap<usize, Weak<T>>> {
|
||||
MutexGuard::map(self.0.lock(), |r| r.get_or_insert_with(FxHashMap::default))
|
||||
Registry(MaybeLazy::Lazy(LazyLock::new(DashMap::default)))
|
||||
}
|
||||
pub fn add(&self, t: T) -> Arc<T>
|
||||
where
|
||||
@@ -24,30 +23,29 @@ impl<T: Send + Sync + ?Sized> Registry<T> {
|
||||
t_arc
|
||||
}
|
||||
pub fn add_raw(&self, t: &Arc<T>) {
|
||||
self.lock()
|
||||
self.0
|
||||
.insert(Arc::as_ptr(t) as *const () as usize, Arc::downgrade(t));
|
||||
}
|
||||
pub fn contains(&self, t: &T) -> bool {
|
||||
self.lock()
|
||||
self.0
|
||||
.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>>) {
|
||||
let old = old.lock();
|
||||
let new = new.lock();
|
||||
|
||||
let mut added = 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 !old.contains_key(id) {
|
||||
if !old.0.contains_key(id) {
|
||||
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 !new.contains_key(id) {
|
||||
if !new.0.contains_key(id) {
|
||||
removed.push(entry);
|
||||
}
|
||||
}
|
||||
@@ -55,52 +53,48 @@ impl<T: Send + Sync + ?Sized> Registry<T> {
|
||||
(added, removed)
|
||||
}
|
||||
pub fn get_valid_contents(&self) -> Vec<Arc<T>> {
|
||||
self.lock()
|
||||
self.0
|
||||
.iter()
|
||||
.filter_map(|pair| pair.1.upgrade())
|
||||
.filter_map(|pair| pair.value().upgrade())
|
||||
.collect()
|
||||
}
|
||||
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>> {
|
||||
self.0
|
||||
.lock()
|
||||
.take()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter_map(|pair| pair.1.upgrade())
|
||||
.collect()
|
||||
let contents = self.get_valid_contents();
|
||||
self.0.clear();
|
||||
contents
|
||||
}
|
||||
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 {
|
||||
// why would we want to retain things we can't upgrade?
|
||||
return true;
|
||||
};
|
||||
(f)(&v)
|
||||
})
|
||||
}
|
||||
pub fn remove(&self, t: &T) {
|
||||
self.lock()
|
||||
.remove(&(ptr::addr_of!(*t) as *const () as usize));
|
||||
self.0.remove(&(ptr::addr_of!(*t) as *const () as usize));
|
||||
}
|
||||
pub fn clear(&self) {
|
||||
self.lock().clear();
|
||||
self.0.clear();
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let registry = self.0.lock();
|
||||
let Some(registry) = &*registry else {
|
||||
return true;
|
||||
};
|
||||
if registry.is_empty() {
|
||||
if self.0.is_empty() {
|
||||
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> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Mutex::new(self.0.lock().clone()))
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
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>>>>);
|
||||
|
||||
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::alias::get_original;
|
||||
use crate::{core::client::Client, nodes::Message};
|
||||
use color_eyre::eyre::Result;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Serialize;
|
||||
@@ -11,13 +10,13 @@ use stardust_xr::scenegraph::ScenegraphError;
|
||||
use stardust_xr::schemas::flex::serialize;
|
||||
use std::future::Future;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{Arc, OnceLock, Weak};
|
||||
use tokio::sync::oneshot;
|
||||
use tracing::{debug, debug_span};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scenegraph {
|
||||
pub(super) client: OnceCell<Weak<Client>>,
|
||||
pub(super) client: OnceLock<Weak<Client>>,
|
||||
nodes: Mutex<FxHashMap<u64, Arc<Node>>>,
|
||||
}
|
||||
|
||||
@@ -59,26 +58,26 @@ impl MethodResponseSender {
|
||||
// ) {
|
||||
// let _ = self.0.send(map_method_return(result));
|
||||
// }
|
||||
pub fn wrap_sync<F: FnOnce() -> color_eyre::eyre::Result<Message>>(self, f: F) {
|
||||
self.send(f().map_err(|e| ScenegraphError::MethodError {
|
||||
pub fn wrap_sync<F: FnOnce() -> crate::core::error::Result<Message>>(self, f: F) {
|
||||
self.send(f().map_err(|e| ScenegraphError::MemberError {
|
||||
error: e.to_string(),
|
||||
}))
|
||||
}
|
||||
pub fn wrap_async<T: Serialize>(
|
||||
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)) });
|
||||
}
|
||||
}
|
||||
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> {
|
||||
let (value, fds) = result.map_err(|e| ScenegraphError::MethodError {
|
||||
let (value, fds) = result.map_err(|e| ScenegraphError::MemberError {
|
||||
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}"),
|
||||
})?;
|
||||
Ok((serialized_value, fds))
|
||||
@@ -86,19 +85,21 @@ fn map_method_return<T: Serialize>(
|
||||
impl scenegraph::Scenegraph for Scenegraph {
|
||||
fn send_signal(
|
||||
&self,
|
||||
node: u64,
|
||||
node_id: u64,
|
||||
aspect_id: u64,
|
||||
method: u64,
|
||||
data: &[u8],
|
||||
fds: Vec<OwnedFd>,
|
||||
) -> Result<(), ScenegraphError> {
|
||||
let Some(client) = self.get_client() else {
|
||||
return Err(ScenegraphError::SignalNotFound);
|
||||
return Err(ScenegraphError::NodeNotFound);
|
||||
};
|
||||
debug_span!("Handle signal", node, method).in_scope(|| {
|
||||
self.get_node(node)
|
||||
debug_span!("Handle signal", aspect_id, node_id, method).in_scope(|| {
|
||||
self.get_node(node_id)
|
||||
.ok_or(ScenegraphError::NodeNotFound)?
|
||||
.send_local_signal(
|
||||
client,
|
||||
aspect_id,
|
||||
method,
|
||||
Message {
|
||||
data: data.to_vec(),
|
||||
@@ -109,23 +110,25 @@ impl scenegraph::Scenegraph for Scenegraph {
|
||||
}
|
||||
fn execute_method(
|
||||
&self,
|
||||
node: u64,
|
||||
node_id: u64,
|
||||
aspect_id: u64,
|
||||
method: u64,
|
||||
data: &[u8],
|
||||
fds: Vec<OwnedFd>,
|
||||
response: oneshot::Sender<Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError>>,
|
||||
) {
|
||||
let Some(client) = self.get_client() else {
|
||||
let _ = response.send(Err(ScenegraphError::MethodNotFound));
|
||||
let _ = response.send(Err(ScenegraphError::NodeNotFound));
|
||||
return;
|
||||
};
|
||||
debug!(node, method, "Handle method");
|
||||
let Some(node) = self.get_node(node) else {
|
||||
debug!(aspect_id, node_id, method, "Handle method");
|
||||
let Some(node) = self.get_node(node_id) else {
|
||||
let _ = response.send(Err(ScenegraphError::NodeNotFound));
|
||||
return;
|
||||
};
|
||||
node.execute_local_method(
|
||||
client,
|
||||
aspect_id,
|
||||
method,
|
||||
Message {
|
||||
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 clap::Parser;
|
||||
use core::client::Client;
|
||||
use core::client::{Client, tick_internal_client};
|
||||
use core::task;
|
||||
use directories::ProjectDirs;
|
||||
use objects::ServerObjects;
|
||||
use once_cell::sync::OnceCell;
|
||||
use session::{launch_start, save_session};
|
||||
use stardust_xr::schemas::dbus::object_registry::ObjectRegistry;
|
||||
use stardust_xr::server;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::time::Duration;
|
||||
use stereokit_rust::material::Material;
|
||||
use stereokit_rust::shader::Shader;
|
||||
use stereokit_rust::sk::{sk_quit, AppMode, DepthMode, OriginMode, QuitReason, SkSettings};
|
||||
use stereokit_rust::system::{LogLevel, Renderer};
|
||||
use stereokit_rust::sk::{
|
||||
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::ui::Ui;
|
||||
use stereokit_rust::util::{Color128, Time};
|
||||
use stereokit_rust::util::{Color128, SphericalHarmonics, Time};
|
||||
use tokio::net::UnixListener;
|
||||
use tokio::sync::Notify;
|
||||
use tracing::metadata::LevelFilter;
|
||||
use tracing::{debug_span, error, info};
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
use zbus::fdo::ObjectManager;
|
||||
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
||||
use zbus::Connection;
|
||||
use zbus::fdo::ObjectManager;
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[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/`.
|
||||
#[clap(id = "SESSION_ID", long = "restore", action)]
|
||||
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() {
|
||||
color_eyre::install().unwrap();
|
||||
|
||||
@@ -91,11 +97,29 @@ async fn main() {
|
||||
.with_thread_names(true)
|
||||
.with_ansi(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();
|
||||
|
||||
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 =
|
||||
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");
|
||||
@@ -120,7 +144,9 @@ async fn main() {
|
||||
|
||||
let project_dirs = ProjectDirs::from("", "", "stardust");
|
||||
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()
|
||||
@@ -129,7 +155,9 @@ async fn main() {
|
||||
dbus_connection
|
||||
.request_name("org.stardustxr.HMD")
|
||||
.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
|
||||
.object_server()
|
||||
@@ -137,13 +165,25 @@ async fn main() {
|
||||
.await
|
||||
.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 stereokit_loop = tokio::task::spawn_blocking({
|
||||
let sk_ready_notifier = sk_ready_notifier.clone();
|
||||
let project_dirs = project_dirs.clone();
|
||||
let cli_args = cli_args.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;
|
||||
let mut startup_children = project_dirs
|
||||
@@ -166,14 +206,19 @@ async fn main() {
|
||||
info!("Cleanly shut down Stardust");
|
||||
}
|
||||
|
||||
static DEFAULT_SKYTEX: OnceLock<Tex> = OnceLock::new();
|
||||
static DEFAULT_SKYLIGHT: OnceLock<SphericalHarmonics> = OnceLock::new();
|
||||
|
||||
fn stereokit_loop(
|
||||
sk_ready_notifier: Arc<Notify>,
|
||||
project_dirs: Option<ProjectDirs>,
|
||||
args: CliArgs,
|
||||
dbus_connection: Connection,
|
||||
object_registry: ObjectRegistry,
|
||||
) {
|
||||
let sk = SkSettings::default()
|
||||
.app_name("Stardust XR")
|
||||
.blend_preference(DisplayBlend::AnyTransparent)
|
||||
.mode(if args.flatscreen {
|
||||
AppMode::Simulator
|
||||
} else {
|
||||
@@ -201,23 +246,33 @@ fn stereokit_loop(
|
||||
Material::default().shader(Shader::pbr_clip());
|
||||
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
|
||||
{
|
||||
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
|
||||
.as_ref()
|
||||
.map(|dirs| dirs.config_dir().join("skytex.hdr"))
|
||||
.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();
|
||||
} else {
|
||||
Renderer::skytex(Tex::gen_color(
|
||||
Color128::BLACK,
|
||||
1,
|
||||
1,
|
||||
TexType::Cubemap,
|
||||
TexFormat::RGBA32,
|
||||
));
|
||||
Renderer::skytex(DEFAULT_SKYTEX.get().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +286,7 @@ fn stereokit_loop(
|
||||
let mut objects = ServerObjects::new(
|
||||
dbus_connection.clone(),
|
||||
&sk,
|
||||
[left_hand_material, right_hand_material],
|
||||
args.disable_controllers,
|
||||
args.disable_hands,
|
||||
);
|
||||
@@ -246,7 +302,7 @@ fn stereokit_loop(
|
||||
wayland.frame_event();
|
||||
destroy_queue::clear();
|
||||
|
||||
objects.update(&sk, token);
|
||||
objects.update(&sk, token, &dbus_connection, &object_registry);
|
||||
input::process_input();
|
||||
nodes::root::Root::send_frame_events(Time::get_step_unscaled());
|
||||
adaptive_sleep(
|
||||
@@ -255,6 +311,7 @@ fn stereokit_loop(
|
||||
Duration::from_micros(250),
|
||||
);
|
||||
|
||||
tick_internal_client();
|
||||
#[cfg(feature = "wayland")]
|
||||
wayland.update();
|
||||
drawable::draw(token);
|
||||
@@ -262,8 +319,6 @@ fn stereokit_loop(
|
||||
}
|
||||
|
||||
info!("Cleanly shut down StereoKit");
|
||||
#[cfg(feature = "wayland")]
|
||||
drop(wayland);
|
||||
}
|
||||
|
||||
fn adaptive_sleep(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::{Aspect, Node};
|
||||
use crate::core::{client::Client, registry::Registry};
|
||||
use color_eyre::eyre::Result;
|
||||
use super::{Aspect, AspectIdentifier, Node};
|
||||
use crate::core::{client::Client, error::Result, registry::Registry};
|
||||
use std::{
|
||||
ops::Add,
|
||||
sync::{Arc, Weak},
|
||||
@@ -68,8 +67,31 @@ impl Alias {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl AspectIdentifier for Alias {
|
||||
const ID: u64 = 0;
|
||||
}
|
||||
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>> {
|
||||
@@ -106,7 +128,7 @@ impl AliasList {
|
||||
.into_iter()
|
||||
.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| {
|
||||
let Some(node) = get_original(node.clone(), false) else {
|
||||
return false;
|
||||
@@ -120,7 +142,7 @@ impl AliasList {
|
||||
pub fn get_aliases(&self) -> Vec<Arc<Node>> {
|
||||
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| {
|
||||
let Some(original) = get_original(node.clone(), false) else {
|
||||
return false;
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
use super::{Aspect, Node};
|
||||
use super::{Aspect, AspectIdentifier, Node};
|
||||
use crate::core::client::Client;
|
||||
use crate::core::destroy_queue;
|
||||
use crate::core::error::Result;
|
||||
use crate::core::registry::Registry;
|
||||
use crate::core::resource::get_resource_file;
|
||||
use crate::create_interface;
|
||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||
use crate::nodes::spatial::{Spatial, Transform};
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use glam::{vec3, Vec4Swizzles};
|
||||
use once_cell::sync::OnceCell;
|
||||
use crate::nodes::spatial::{SPATIAL_ASPECT_ALIAS_INFO, Spatial, Transform};
|
||||
use color_eyre::eyre::eyre;
|
||||
use glam::{Vec4Swizzles, vec3};
|
||||
use parking_lot::Mutex;
|
||||
use stardust_xr::values::ResourceID;
|
||||
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::{ffi::OsStr, path::PathBuf};
|
||||
use stereokit_rust::sound::{Sound as SkSound, SoundInst};
|
||||
|
||||
@@ -25,7 +23,7 @@ pub struct Sound {
|
||||
|
||||
volume: f32,
|
||||
pending_audio_path: PathBuf,
|
||||
sk_sound: OnceCell<SkSound>,
|
||||
sk_sound: OnceLock<SkSound>,
|
||||
instance: Mutex<Option<SoundInst>>,
|
||||
stop: Mutex<Option<()>>,
|
||||
play: Mutex<Option<()>>,
|
||||
@@ -42,14 +40,13 @@ impl Sound {
|
||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||
volume: 1.0,
|
||||
pending_audio_path,
|
||||
sk_sound: OnceCell::new(),
|
||||
sk_sound: OnceLock::new(),
|
||||
instance: Mutex::new(None),
|
||||
stop: Mutex::new(None),
|
||||
play: Mutex::new(None),
|
||||
};
|
||||
let sound_arc = SOUND_REGISTRY.add(sound);
|
||||
node.add_aspect_raw(sound_arc.clone());
|
||||
<Sound as SoundAspect>::add_node_members(node);
|
||||
Ok(sound_arc)
|
||||
}
|
||||
|
||||
@@ -71,8 +68,11 @@ impl Sound {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl AspectIdentifier for Sound {
|
||||
impl_aspect_for_sound_aspect_id! {}
|
||||
}
|
||||
impl Aspect for Sound {
|
||||
const NAME: &'static str = "Sound";
|
||||
impl_aspect_for_sound_aspect! {}
|
||||
}
|
||||
impl SoundAspect for Sound {
|
||||
fn play(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||
@@ -88,6 +88,9 @@ impl SoundAspect for Sound {
|
||||
}
|
||||
impl Drop for Sound {
|
||||
fn drop(&mut self) {
|
||||
if let Some(instance) = self.instance.lock().take() {
|
||||
instance.stop();
|
||||
}
|
||||
if let Some(sk_sound) = self.sk_sound.take() {
|
||||
destroy_queue::add(sk_sound);
|
||||
}
|
||||
@@ -101,9 +104,7 @@ pub fn update() {
|
||||
}
|
||||
}
|
||||
|
||||
create_interface!(AudioInterface);
|
||||
struct AudioInterface;
|
||||
impl InterfaceAspect for AudioInterface {
|
||||
impl InterfaceAspect for Interface {
|
||||
#[doc = "Create a sound node. WAV and MP3 are supported."]
|
||||
fn create_sound(
|
||||
_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 crate::{
|
||||
core::{client::Client, registry::Registry},
|
||||
nodes::{spatial::Spatial, Aspect, Node},
|
||||
core::{client::Client, error::Result, registry::Registry},
|
||||
nodes::{Node, spatial::Spatial},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Vec3;
|
||||
use glam::{FloatExt, Vec3};
|
||||
use parking_lot::Mutex;
|
||||
use prisma::Lerp;
|
||||
use std::{collections::VecDeque, sync::Arc};
|
||||
use stereokit_rust::{
|
||||
maths::Bounds, sk::MainThreadToken, system::LinePoint as SkLinePoint, util::Color128,
|
||||
@@ -40,7 +38,6 @@ impl Lines {
|
||||
space: node.get_aspect::<Spatial>()?.clone(),
|
||||
data: Mutex::new(lines),
|
||||
});
|
||||
<Lines as LinesAspect>::add_node_members(node);
|
||||
node.add_aspect_raw(lines.clone());
|
||||
|
||||
Ok(lines)
|
||||
@@ -64,10 +61,10 @@ impl Lines {
|
||||
let last = line.points.last().unwrap();
|
||||
|
||||
let color = Color128 {
|
||||
r: first.color.c.r.lerp(&last.color.c.r, 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),
|
||||
a: first.color.a.lerp(&last.color.a, 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),
|
||||
b: first.color.c.b.lerp(last.color.c.b, 0.5),
|
||||
a: first.color.a.lerp(last.color.a, 0.5),
|
||||
};
|
||||
let connect_point = SkLinePoint {
|
||||
pt: transform_mat
|
||||
@@ -83,9 +80,6 @@ impl Lines {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Aspect for Lines {
|
||||
const NAME: &'static str = "Lines";
|
||||
}
|
||||
impl LinesAspect for Lines {
|
||||
fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> {
|
||||
let lines_aspect = node.get_aspect::<Lines>()?;
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
pub mod lines;
|
||||
pub mod model;
|
||||
#[cfg(feature = "wayland")]
|
||||
pub mod shader_manipulation;
|
||||
pub mod shaders;
|
||||
pub mod text;
|
||||
|
||||
use self::{lines::Lines, model::Model, text::Text};
|
||||
use super::{
|
||||
Aspect, AspectIdentifier, Node,
|
||||
spatial::{Spatial, Transform},
|
||||
Node,
|
||||
};
|
||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||
use crate::{DEFAULT_SKYLIGHT, nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO};
|
||||
use crate::{
|
||||
core::{client::Client, resource::get_resource_file},
|
||||
create_interface,
|
||||
DEFAULT_SKYTEX,
|
||||
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 stardust_xr::values::ResourceID;
|
||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
||||
@@ -26,30 +25,72 @@ pub fn draw(token: &MainThreadToken) {
|
||||
lines::draw_all(token);
|
||||
model::draw_all(token);
|
||||
text::draw_all(token);
|
||||
|
||||
if let Some(skytex) = QUEUED_SKYTEX.lock().take() {
|
||||
if let Ok(skytex) = SHCubemap::from_cubemap_equirectangular(skytex, true, 100) {
|
||||
Renderer::skytex(skytex.tex);
|
||||
match QUEUED_SKYTEX.lock().take() {
|
||||
Some(Some(skytex)) => {
|
||||
if let Ok(skytex) = SHCubemap::from_cubemap(skytex, true, 100) {
|
||||
Renderer::skytex(skytex.tex);
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
Renderer::skytex(DEFAULT_SKYTEX.get().unwrap());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if let Some(skylight) = QUEUED_SKYLIGHT.lock().take() {
|
||||
if let Ok(skylight) = SHCubemap::from_cubemap_equirectangular(skylight, true, 100) {
|
||||
Renderer::skylight(skylight.sh);
|
||||
match QUEUED_SKYLIGHT.lock().take() {
|
||||
Some(Some(skylight)) => {
|
||||
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_SKYTEX: Mutex<Option<PathBuf>> = Mutex::new(None);
|
||||
static QUEUED_SKYLIGHT: Mutex<Option<Option<PathBuf>>> = Mutex::new(None);
|
||||
static QUEUED_SKYTEX: Mutex<Option<Option<PathBuf>>> = Mutex::new(None);
|
||||
|
||||
stardust_xr_server_codegen::codegen_drawable_protocol!();
|
||||
create_interface!(DrawableInterface);
|
||||
|
||||
pub struct DrawableInterface;
|
||||
impl InterfaceAspect for DrawableInterface {
|
||||
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")])
|
||||
.ok_or(eyre::eyre!("Could not find resource"))?;
|
||||
impl AspectIdentifier for Lines {
|
||||
impl_aspect_for_lines_aspect_id! {}
|
||||
}
|
||||
impl Aspect for Lines {
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
@@ -57,10 +98,14 @@ impl InterfaceAspect for DrawableInterface {
|
||||
fn set_sky_light(
|
||||
_node: Arc<Node>,
|
||||
calling_client: Arc<Client>,
|
||||
light: ResourceID,
|
||||
light: Option<ResourceID>,
|
||||
) -> Result<()> {
|
||||
let resource_path = get_resource_file(&light, &calling_client, &[OsStr::new("hdr")])
|
||||
.ok_or(eyre::eyre!("Could not find resource"))?;
|
||||
let resource_path = light
|
||||
.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);
|
||||
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::error::Result;
|
||||
use crate::core::registry::Registry;
|
||||
use crate::core::resource::get_resource_file;
|
||||
use crate::nodes::Node;
|
||||
use crate::nodes::alias::{Alias, AliasList};
|
||||
use crate::nodes::spatial::Spatial;
|
||||
use crate::nodes::{Aspect, Node};
|
||||
use color_eyre::eyre::{bail, eyre, Result};
|
||||
use color_eyre::eyre::eyre;
|
||||
use glam::{Mat4, Vec2, Vec3};
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stardust_xr::values::ResourceID;
|
||||
use std::ffi::OsStr;
|
||||
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::maths::Bounds;
|
||||
use stereokit_rust::sk::MainThreadToken;
|
||||
use stereokit_rust::{material::Material, model::Model as SKModel, tex::Tex, util::Color128};
|
||||
|
||||
pub struct MaterialWrapper(pub Material);
|
||||
impl Drop for MaterialWrapper {
|
||||
fn drop(&mut self) {
|
||||
MATERIAL_REGISTRY.remove(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for MaterialWrapper {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.get_shader().0.as_ptr().hash(state);
|
||||
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)
|
||||
}
|
||||
@@ -57,10 +65,9 @@ unsafe impl Send for MaterialWrapper {}
|
||||
unsafe impl Sync for MaterialWrapper {}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MaterialRegistry(Mutex<FxHashMap<u64, String>>);
|
||||
struct MaterialRegistry(Mutex<FxHashMap<u64, Weak<MaterialWrapper>>>);
|
||||
impl MaterialRegistry {
|
||||
fn add_or_get(&self, material: Arc<MaterialWrapper>) -> Arc<MaterialWrapper> {
|
||||
let mut lock = self.0.lock();
|
||||
let hash = {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
@@ -68,20 +75,31 @@ impl MaterialRegistry {
|
||||
hasher.finish()
|
||||
};
|
||||
|
||||
if let Some(id) = lock.get(&hash) {
|
||||
if let Ok(existing) = Material::find(id) {
|
||||
return Arc::new(MaterialWrapper(existing));
|
||||
let mut lock = self.0.lock();
|
||||
if let Some(mat) = lock.get(&hash) {
|
||||
if let Some(mat) = mat.upgrade() {
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
|
||||
lock.insert(hash, material.0.get_id().to_string());
|
||||
lock.insert(hash, Arc::downgrade(&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 HOLDOUT_MATERIAL: OnceCell<Arc<MaterialWrapper>> = OnceCell::new();
|
||||
static HOLDOUT_MATERIAL: OnceLock<Arc<MaterialWrapper>> = OnceLock::new();
|
||||
|
||||
impl MaterialParameter {
|
||||
fn apply_to_material(&self, client: &Client, material: &Material, parameter_name: &str) {
|
||||
@@ -130,6 +148,7 @@ pub struct ModelPart {
|
||||
path: String,
|
||||
space: Arc<Spatial>,
|
||||
model: Weak<Model>,
|
||||
material: Mutex<Option<Arc<MaterialWrapper>>>,
|
||||
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
|
||||
pending_material_replacement: Mutex<Option<Arc<MaterialWrapper>>>,
|
||||
aliases: AliasList,
|
||||
@@ -203,8 +222,8 @@ impl ModelPart {
|
||||
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
||||
pending_material_replacement: Mutex::new(None),
|
||||
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());
|
||||
parts.push(model_part.clone());
|
||||
Some(model_part)
|
||||
@@ -230,7 +249,10 @@ impl ModelPart {
|
||||
};
|
||||
let shared_material =
|
||||
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(replacement.copy())));
|
||||
|
||||
let mut lock = self.material.lock();
|
||||
part.material(&shared_material.0);
|
||||
lock.replace(shared_material);
|
||||
}
|
||||
|
||||
fn update(&self) {
|
||||
@@ -257,7 +279,9 @@ impl ModelPart {
|
||||
};
|
||||
|
||||
if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
|
||||
let mut lock = self.material.lock();
|
||||
part.material(&material_replacement.0);
|
||||
lock.replace(material_replacement);
|
||||
}
|
||||
|
||||
'mat_params: {
|
||||
@@ -273,14 +297,13 @@ impl ModelPart {
|
||||
|
||||
let shared_material =
|
||||
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(new_material)));
|
||||
let mut lock = self.material.lock();
|
||||
part.material(&shared_material.0);
|
||||
lock.replace(shared_material);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Aspect for ModelPart {
|
||||
const NAME: &'static str = "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."]
|
||||
fn apply_holdout_material(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||
@@ -309,7 +332,7 @@ impl ModelPartAspect for ModelPart {
|
||||
pub struct Model {
|
||||
space: Arc<Spatial>,
|
||||
_resource_id: ResourceID,
|
||||
sk_model: OnceCell<SKModel>,
|
||||
sk_model: OnceLock<SKModel>,
|
||||
parts: Mutex<Vec<Arc<ModelPart>>>,
|
||||
}
|
||||
impl Model {
|
||||
@@ -324,10 +347,9 @@ impl Model {
|
||||
let model = Arc::new(Model {
|
||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||
_resource_id: resource_id,
|
||||
sk_model: OnceCell::new(),
|
||||
sk_model: OnceLock::new(),
|
||||
parts: Mutex::new(Vec::default()),
|
||||
});
|
||||
<Model as ModelAspect>::add_node_members(node);
|
||||
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
|
||||
@@ -361,9 +383,6 @@ impl Model {
|
||||
// TODO: proper hread safety in stereokit_rust (probably just bind stereokit directly)
|
||||
unsafe impl Send for Model {}
|
||||
unsafe impl Sync for Model {}
|
||||
impl Aspect for Model {
|
||||
const NAME: &'static str = "Model";
|
||||
}
|
||||
impl ModelAspect for Model {
|
||||
#[doc = "Bind a model part to the node with the ID input."]
|
||||
fn bind_model_part(
|
||||
@@ -371,12 +390,12 @@ impl ModelAspect for Model {
|
||||
calling_client: Arc<Client>,
|
||||
id: u64,
|
||||
part_path: String,
|
||||
) -> color_eyre::eyre::Result<()> {
|
||||
) -> Result<()> {
|
||||
let model = node.get_aspect::<Model>()?;
|
||||
let parts = model.parts.lock();
|
||||
let Some(part) = parts.iter().find(|p| p.path == part_path) else {
|
||||
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(
|
||||
&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::{
|
||||
core::{client::Client, destroy_queue, registry::Registry, resource::get_resource_file},
|
||||
nodes::{spatial::Spatial, Aspect, Node},
|
||||
core::{
|
||||
client::Client, destroy_queue, error::Result, registry::Registry,
|
||||
resource::get_resource_file,
|
||||
},
|
||||
nodes::{Node, spatial::Spatial},
|
||||
};
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use glam::{vec3, Mat4, Vec2};
|
||||
use once_cell::sync::OnceCell;
|
||||
use color_eyre::eyre::eyre;
|
||||
use glam::{Mat4, Vec2, vec3};
|
||||
use parking_lot::Mutex;
|
||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
path::PathBuf,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use stereokit_rust::{
|
||||
font::Font,
|
||||
sk::MainThreadToken,
|
||||
system::{TextAlign, TextFit, TextStyle as SkTextStyle},
|
||||
util::{Color128, Color32},
|
||||
util::{Color32, Color128},
|
||||
};
|
||||
|
||||
use super::{TextAspect, TextStyle};
|
||||
@@ -35,7 +41,7 @@ fn convert_align(x_align: super::XAlign, y_align: super::YAlign) -> TextAlign {
|
||||
pub struct Text {
|
||||
space: Arc<Spatial>,
|
||||
font_path: Option<PathBuf>,
|
||||
style: OnceCell<SkTextStyle>,
|
||||
style: OnceLock<SkTextStyle>,
|
||||
|
||||
text: Mutex<String>,
|
||||
data: Mutex<TextStyle>,
|
||||
@@ -48,93 +54,85 @@ impl Text {
|
||||
font_path: style.font.as_ref().and_then(|res| {
|
||||
get_resource_file(res, &client, &[OsStr::new("ttf"), OsStr::new("otf")])
|
||||
}),
|
||||
style: OnceCell::new(),
|
||||
style: OnceLock::new(),
|
||||
|
||||
text: Mutex::new(text),
|
||||
data: Mutex::new(style),
|
||||
});
|
||||
<Text as TextAspect>::add_node_members(node);
|
||||
node.add_aspect_raw(text.clone());
|
||||
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
fn draw(&self, token: &MainThreadToken) {
|
||||
let style =
|
||||
self.style
|
||||
.get_or_try_init(|| -> Result<SkTextStyle, color_eyre::eyre::Error> {
|
||||
let font = self
|
||||
.font_path
|
||||
.as_deref()
|
||||
.and_then(|path| Font::from_file(path).ok())
|
||||
.unwrap_or_default();
|
||||
Ok(SkTextStyle::from_font(font, 1.0, Color32::WHITE))
|
||||
});
|
||||
let style = self.style.get_or_init(|| {
|
||||
let font = self
|
||||
.font_path
|
||||
.as_deref()
|
||||
.and_then(|path| Font::from_file(path).ok())
|
||||
.unwrap_or_default();
|
||||
SkTextStyle::from_font(font, 1.0, Color32::WHITE)
|
||||
});
|
||||
|
||||
if let Ok(style) = style {
|
||||
let text = self.text.lock();
|
||||
let data = self.data.lock();
|
||||
let transform = self.space.global_transform()
|
||||
* Mat4::from_scale(vec3(
|
||||
data.character_height,
|
||||
data.character_height,
|
||||
data.character_height,
|
||||
));
|
||||
if let Some(bounds) = &data.bounds {
|
||||
stereokit_rust::system::Text::add_in(
|
||||
token,
|
||||
&*text,
|
||||
transform,
|
||||
Vec2::from(bounds.bounds) / data.character_height,
|
||||
match bounds.fit {
|
||||
super::TextFit::Wrap => TextFit::Wrap,
|
||||
super::TextFit::Clip => TextFit::Clip,
|
||||
super::TextFit::Squeeze => TextFit::Squeeze,
|
||||
super::TextFit::Exact => TextFit::Exact,
|
||||
super::TextFit::Overflow => TextFit::Overflow,
|
||||
},
|
||||
Some(*style),
|
||||
Some(Color128::new(
|
||||
data.color.c.r,
|
||||
data.color.c.g,
|
||||
data.color.c.b,
|
||||
data.color.a,
|
||||
)),
|
||||
data.bounds
|
||||
.as_ref()
|
||||
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
||||
Some(convert_align(data.text_align_x, data.text_align_y)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
stereokit_rust::system::Text::add_at(
|
||||
token,
|
||||
&*text,
|
||||
transform,
|
||||
Some(*style),
|
||||
Some(Color128::new(
|
||||
data.color.c.r,
|
||||
data.color.c.g,
|
||||
data.color.c.b,
|
||||
data.color.a,
|
||||
)),
|
||||
data.bounds
|
||||
.as_ref()
|
||||
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
||||
Some(convert_align(data.text_align_x, data.text_align_y)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
let text = self.text.lock();
|
||||
let data = self.data.lock();
|
||||
let transform = self.space.global_transform()
|
||||
* Mat4::from_scale(vec3(
|
||||
data.character_height,
|
||||
data.character_height,
|
||||
data.character_height,
|
||||
));
|
||||
if let Some(bounds) = &data.bounds {
|
||||
stereokit_rust::system::Text::add_in(
|
||||
token,
|
||||
&*text,
|
||||
transform,
|
||||
Vec2::from(bounds.bounds) / data.character_height,
|
||||
match bounds.fit {
|
||||
super::TextFit::Wrap => TextFit::Wrap,
|
||||
super::TextFit::Clip => TextFit::Clip,
|
||||
super::TextFit::Squeeze => TextFit::Squeeze,
|
||||
super::TextFit::Exact => TextFit::Exact,
|
||||
super::TextFit::Overflow => TextFit::Overflow,
|
||||
},
|
||||
Some(*style),
|
||||
Some(Color128::new(
|
||||
data.color.c.r,
|
||||
data.color.c.g,
|
||||
data.color.c.b,
|
||||
data.color.a,
|
||||
)),
|
||||
data.bounds
|
||||
.as_ref()
|
||||
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
||||
Some(convert_align(data.text_align_x, data.text_align_y)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
stereokit_rust::system::Text::add_at(
|
||||
token,
|
||||
&*text,
|
||||
transform,
|
||||
Some(*style),
|
||||
Some(Color128::new(
|
||||
data.color.c.r,
|
||||
data.color.c.g,
|
||||
data.color.c.b,
|
||||
data.color.a,
|
||||
)),
|
||||
data.bounds
|
||||
.as_ref()
|
||||
.map(|b| convert_align(b.anchor_align_x, b.anchor_align_y)),
|
||||
Some(convert_align(data.text_align_x, data.text_align_y)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Aspect for Text {
|
||||
const NAME: &'static str = "Text";
|
||||
}
|
||||
impl TextAspect for Text {
|
||||
fn set_character_height(
|
||||
node: Arc<Node>,
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
use super::alias::{Alias, AliasInfo};
|
||||
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,
|
||||
};
|
||||
use super::{Aspect, Node};
|
||||
use super::{Aspect, AspectIdentifier, Node};
|
||||
use crate::core::client::Client;
|
||||
use crate::create_interface;
|
||||
use crate::nodes::spatial::Transform;
|
||||
use crate::core::error::Result;
|
||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||
use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO;
|
||||
use color_eyre::eyre::{OptionExt, Result};
|
||||
use glam::{vec2, vec3, vec3a, Vec3, Vec3A, Vec3Swizzles};
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::nodes::spatial::Transform;
|
||||
use color_eyre::eyre::OptionExt;
|
||||
use glam::{Vec3, Vec3A, Vec3Swizzles, vec2, vec3, vec3a};
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
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
|
||||
|
||||
pub static FIELD_ALIAS_INFO: Lazy<AliasInfo> = Lazy::new(|| AliasInfo {
|
||||
pub static FIELD_ALIAS_INFO: LazyLock<AliasInfo> = LazyLock::new(|| AliasInfo {
|
||||
server_methods: vec![
|
||||
SPATIAL_REF_GET_TRANSFORM_SERVER_OPCODE,
|
||||
SPATIAL_REF_GET_LOCAL_BOUNDING_BOX_SERVER_OPCODE,
|
||||
@@ -145,15 +145,65 @@ impl Field {
|
||||
shape: Mutex::new(shape),
|
||||
};
|
||||
let field = node.add_aspect(field);
|
||||
<Field as FieldRefAspect>::add_node_members(node);
|
||||
<Field as FieldAspect>::add_node_members(node);
|
||||
node.add_aspect(FieldRef);
|
||||
Ok(field)
|
||||
}
|
||||
}
|
||||
impl Aspect for Field {
|
||||
const NAME: &'static str = "Field";
|
||||
impl AspectIdentifier for 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(
|
||||
node: Arc<Node>,
|
||||
_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> {
|
||||
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 {
|
||||
impl InterfaceAspect for Interface {
|
||||
async fn import_field_ref(
|
||||
_node: Arc<Node>,
|
||||
calling_client: Arc<Client>,
|
||||
uid: u64,
|
||||
) -> Result<Arc<Node>> {
|
||||
EXPORTED_FIELDS
|
||||
Ok(EXPORTED_FIELDS
|
||||
.lock()
|
||||
.get(&uid)
|
||||
.map(|s| {
|
||||
@@ -266,7 +274,7 @@ impl InterfaceAspect for FieldInterface {
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.ok_or_eyre("Couldn't find spatial with that ID")
|
||||
.ok_or_eyre("Couldn't find spatial with that ID")?)
|
||||
}
|
||||
|
||||
fn create_field(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{Finger, Hand, InputDataTrait, InputHandler, InputMethod, Joint, Thumb};
|
||||
use crate::nodes::fields::{Field, FieldTrait};
|
||||
use crate::nodes::spatial::Spatial;
|
||||
use glam::{vec3a, Mat4, Quat};
|
||||
use glam::{Mat4, Quat, vec3a};
|
||||
use std::sync::Arc;
|
||||
|
||||
impl Default for Joint {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{InputHandlerAspect, INPUT_HANDLER_REGISTRY, INPUT_METHOD_REGISTRY};
|
||||
use crate::nodes::{alias::AliasList, fields::Field, spatial::Spatial, Aspect, Node};
|
||||
use super::{INPUT_HANDLER_REGISTRY, INPUT_METHOD_REGISTRY, InputHandlerAspect};
|
||||
use crate::nodes::{Node, alias::AliasList, fields::Field, spatial::Spatial};
|
||||
use color_eyre::eyre::Result;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -23,9 +23,6 @@ impl InputHandler {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Aspect for InputHandler {
|
||||
const NAME: &'static str = "InputHandler";
|
||||
}
|
||||
impl InputHandlerAspect for InputHandler {}
|
||||
impl PartialEq for InputHandler {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use super::{
|
||||
input_method_client, InputData, InputDataTrait, InputDataType, InputHandler, InputMethodAspect,
|
||||
InputMethodRefAspect, INPUT_HANDLER_REGISTRY, INPUT_METHOD_REF_ASPECT_ALIAS_INFO,
|
||||
INPUT_METHOD_REGISTRY,
|
||||
INPUT_HANDLER_REGISTRY, INPUT_METHOD_REF_ASPECT_ALIAS_INFO, INPUT_METHOD_REGISTRY, InputData,
|
||||
InputDataTrait, InputDataType, InputHandler, InputMethodAspect, InputMethodRefAspect,
|
||||
input_method_client,
|
||||
};
|
||||
use crate::{
|
||||
core::{client::Client, registry::Registry},
|
||||
core::{client::Client, error::Result, registry::Registry},
|
||||
nodes::{
|
||||
Node,
|
||||
alias::{Alias, AliasList},
|
||||
fields::{Field, FIELD_ALIAS_INFO},
|
||||
fields::{FIELD_ALIAS_INFO, Field},
|
||||
spatial::Spatial,
|
||||
Aspect, Node,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use parking_lot::Mutex;
|
||||
use stardust_xr::values::Datamap;
|
||||
use std::sync::{Arc, Weak};
|
||||
@@ -25,7 +24,7 @@ pub struct InputMethod {
|
||||
handler_aliases: AliasList,
|
||||
handler_field_aliases: AliasList,
|
||||
pub(super) handler_order: Mutex<Vec<Weak<InputHandler>>>,
|
||||
pub internal_capture_requests: Registry<InputHandler>,
|
||||
pub capture_attempts: Registry<InputHandler>,
|
||||
pub captures: Registry<InputHandler>,
|
||||
}
|
||||
impl InputMethod {
|
||||
@@ -42,16 +41,15 @@ impl InputMethod {
|
||||
handler_aliases: AliasList::default(),
|
||||
handler_field_aliases: AliasList::default(),
|
||||
handler_order: Mutex::new(Vec::new()),
|
||||
internal_capture_requests: Registry::new(),
|
||||
capture_attempts: 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() {
|
||||
method.handle_new_handler(&handler);
|
||||
}
|
||||
let method = INPUT_METHOD_REGISTRY.add(method);
|
||||
node.add_aspect_raw(method.clone());
|
||||
node.add_aspect(InputMethodRef);
|
||||
Ok(method)
|
||||
}
|
||||
|
||||
@@ -131,6 +129,7 @@ impl InputMethod {
|
||||
self.handler_aliases.remove_aspect(handler);
|
||||
self.handler_field_aliases
|
||||
.remove_aspect(handler.field.as_ref());
|
||||
self.capture_attempts.remove(handler);
|
||||
}
|
||||
|
||||
pub(super) fn serialize(&self, alias_id: u64, handler: &Arc<InputHandler>) -> InputData {
|
||||
@@ -153,24 +152,23 @@ impl InputMethod {
|
||||
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
|
||||
.internal_capture_requests
|
||||
.add_raw(&input_handler);
|
||||
Ok(())
|
||||
pub(super) fn cull_capture_attempts(&self) {
|
||||
let sent = self
|
||||
.handler_order
|
||||
.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 {
|
||||
@@ -231,3 +229,28 @@ impl Drop for InputMethod {
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,15 @@ mod tip;
|
||||
pub use handler::*;
|
||||
pub use method::*;
|
||||
|
||||
use super::Aspect;
|
||||
use super::AspectIdentifier;
|
||||
use super::fields::Field;
|
||||
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_REF_ASPECT_ALIAS_INFO;
|
||||
use crate::{core::client::Client, nodes::Node};
|
||||
use crate::{core::registry::Registry, nodes::spatial::Transform};
|
||||
use color_eyre::eyre::Result;
|
||||
use stardust_xr::values::Datamap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -25,6 +26,25 @@ pub static INPUT_HANDLER_REGISTRY: Registry<InputHandler> = Registry::new();
|
||||
|
||||
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 {
|
||||
fn transform(&mut self, method: &InputMethod, handler: &InputHandler);
|
||||
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
|
||||
@@ -47,9 +67,7 @@ impl InputDataTrait for InputDataType {
|
||||
}
|
||||
}
|
||||
|
||||
create_interface!(InputInterface);
|
||||
pub struct InputInterface;
|
||||
impl InterfaceAspect for InputInterface {
|
||||
impl InterfaceAspect for Interface {
|
||||
#[doc = "Create an input method node"]
|
||||
fn create_input_method(
|
||||
_node: Arc<Node>,
|
||||
@@ -147,6 +165,6 @@ pub fn process_input() {
|
||||
let _ = input_handler_client::input(&handler_node, &methods, &datas);
|
||||
}
|
||||
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},
|
||||
spatial::Spatial,
|
||||
};
|
||||
use glam::{vec3, Mat4, Quat};
|
||||
use glam::{Mat4, Quat, vec3};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
impl Default for Pointer {
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
use super::{
|
||||
create_item_acceptor_flex, register_item_ui_flex, Item, ItemAcceptor, ItemInterface, ItemType,
|
||||
};
|
||||
use super::{Item, ItemType, create_item_acceptor_flex, register_item_ui_flex};
|
||||
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_ASPECT_ALIAS_INFO;
|
||||
use crate::{
|
||||
core::{client::Client, registry::Registry, scenegraph::MethodResponseSender},
|
||||
create_interface,
|
||||
nodes::{
|
||||
Message, Node,
|
||||
drawable::{
|
||||
model::{MaterialWrapper, ModelPart},
|
||||
shaders::UNLIT_SHADER_BYTES,
|
||||
},
|
||||
items::TypeInfo,
|
||||
spatial::{Spatial, Transform},
|
||||
Message, Node,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::{bail, eyre, Result};
|
||||
use glam::Mat4;
|
||||
use lazy_static::lazy_static;
|
||||
use mint::{ColumnMatrix4, Vector2};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use stereokit_rust::{
|
||||
material::{Material, Transparency},
|
||||
shader::Shader,
|
||||
@@ -33,7 +33,6 @@ use stereokit_rust::{
|
||||
tex::{Tex, TexFormat, TexType},
|
||||
util::Color128,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
pub struct TexWrapper(pub Tex);
|
||||
unsafe impl Send for TexWrapper {}
|
||||
@@ -48,6 +47,12 @@ lazy_static! {
|
||||
ui: Default::default(),
|
||||
items: 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| {
|
||||
let _ = camera_item_ui_client::create_acceptor(node, acceptor, acceptor_field);
|
||||
}
|
||||
@@ -62,30 +67,27 @@ struct FrameInfo {
|
||||
pub struct CameraItem {
|
||||
space: Arc<Spatial>,
|
||||
frame_info: Mutex<FrameInfo>,
|
||||
sk_tex: OnceCell<TexWrapper>,
|
||||
sk_mat: OnceCell<Arc<MaterialWrapper>>,
|
||||
sk_tex: OnceLock<TexWrapper>,
|
||||
sk_mat: OnceLock<Arc<MaterialWrapper>>,
|
||||
applied_to: Registry<ModelPart>,
|
||||
apply_to: Registry<ModelPart>,
|
||||
}
|
||||
#[allow(unused)]
|
||||
impl CameraItem {
|
||||
pub fn add_to(node: &Arc<Node>, proj_matrix: Mat4, px_size: Vector2<u32>) {
|
||||
Item::add_to(
|
||||
node,
|
||||
&ITEM_TYPE_INFO_CAMERA,
|
||||
ItemType::Camera(CameraItem {
|
||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||
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(),
|
||||
let item = Arc::new(CameraItem {
|
||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||
frame_info: Mutex::new(FrameInfo {
|
||||
proj_matrix,
|
||||
px_size,
|
||||
}),
|
||||
);
|
||||
// <CameraItem as CameraItemAspect>::node_methods(node);
|
||||
sk_tex: OnceLock::new(),
|
||||
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(
|
||||
@@ -97,7 +99,7 @@ impl CameraItem {
|
||||
response.wrap_sync(move || {
|
||||
let ItemType::Camera(_camera) = &node.get_aspect::<Item>().unwrap().specialization
|
||||
else {
|
||||
return Err(eyre!("Wrong item type?"));
|
||||
bail!("Wrong item type?");
|
||||
};
|
||||
Ok(serialize(())?.into())
|
||||
});
|
||||
@@ -109,7 +111,7 @@ impl CameraItem {
|
||||
message: Message,
|
||||
) -> Result<()> {
|
||||
let ItemType::Camera(camera) = &node.get_aspect::<Item>().unwrap().specialization else {
|
||||
bail!("Wrong item type?")
|
||||
bail!("Wrong item type?");
|
||||
};
|
||||
let model_part_node =
|
||||
calling_client.get_node("Model part", deserialize(&message.data).unwrap())?;
|
||||
@@ -137,19 +139,13 @@ impl CameraItem {
|
||||
TexFormat::RGBA32Linear,
|
||||
))
|
||||
});
|
||||
let sk_mat = self
|
||||
.sk_mat
|
||||
.get_or_try_init(|| -> Result<Arc<MaterialWrapper>> {
|
||||
let shader = Shader::from_memory(UNLIT_SHADER_BYTES)?;
|
||||
let mut mat = Material::new(&shader, None);
|
||||
mat.get_all_param_info().set_texture("diffuse", &sk_tex.0);
|
||||
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;
|
||||
};
|
||||
let sk_mat = self.sk_mat.get_or_init(|| {
|
||||
let shader = Shader::from_memory(UNLIT_SHADER_BYTES).unwrap();
|
||||
let mut mat = Material::new(&shader, None);
|
||||
mat.get_all_param_info().set_texture("diffuse", &sk_tex.0);
|
||||
mat.transparency(Transparency::Blend);
|
||||
Arc::new(MaterialWrapper(mat))
|
||||
});
|
||||
for model_part in self.apply_to.take_valid_contents() {
|
||||
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 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<()> {
|
||||
super::acceptor_capture_item_flex(node, item)
|
||||
}
|
||||
@@ -184,8 +202,7 @@ pub fn update(token: &MainThreadToken) {
|
||||
}
|
||||
}
|
||||
|
||||
create_interface!(ItemInterface);
|
||||
impl InterfaceAspect for ItemInterface {
|
||||
impl InterfaceAspect for Interface {
|
||||
#[doc = "Create a camera item at a specific location"]
|
||||
fn create_camera_item(
|
||||
_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."]
|
||||
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)
|
||||
}
|
||||
|
||||
#[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(
|
||||
_node: Arc<Node>,
|
||||
node: Arc<Node>,
|
||||
calling_client: Arc<Client>,
|
||||
id: u64,
|
||||
parent: Arc<Node>,
|
||||
transform: Transform,
|
||||
field: Arc<Node>,
|
||||
) -> Result<()> {
|
||||
node.add_aspect(CameraItemAcceptor);
|
||||
create_item_acceptor_flex(
|
||||
calling_client,
|
||||
id,
|
||||
|
||||
@@ -4,15 +4,16 @@ pub mod panel;
|
||||
use self::camera::CameraItem;
|
||||
use self::panel::PanelItemTrait;
|
||||
use super::alias::AliasList;
|
||||
use super::fields::{Field, FIELD_ALIAS_INFO};
|
||||
use super::fields::{FIELD_ALIAS_INFO, Field};
|
||||
use super::spatial::Spatial;
|
||||
use super::{Alias, Aspect, Node};
|
||||
use super::{Alias, Aspect, AspectIdentifier, Node};
|
||||
use crate::core::client::Client;
|
||||
use crate::core::error::Result;
|
||||
use crate::core::registry::Registry;
|
||||
use crate::ensure;
|
||||
use crate::nodes::alias::AliasInfo;
|
||||
use crate::nodes::spatial::Transform;
|
||||
use crate::nodes::spatial::SPATIAL_ASPECT_ALIAS_INFO;
|
||||
use color_eyre::eyre::{ensure, Result};
|
||||
use crate::nodes::spatial::Transform;
|
||||
use parking_lot::Mutex;
|
||||
use std::hash::Hash;
|
||||
use std::sync::{Arc, Weak};
|
||||
@@ -47,6 +48,8 @@ pub struct TypeInfo {
|
||||
pub ui: Mutex<Weak<ItemUI>>,
|
||||
pub items: Registry<Item>,
|
||||
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>),
|
||||
}
|
||||
impl Hash for TypeInfo {
|
||||
@@ -81,7 +84,6 @@ impl Item {
|
||||
};
|
||||
let item = type_info.items.add(item);
|
||||
|
||||
<Item as ItemAspect>::add_node_members(node);
|
||||
if let Some(ui) = type_info.ui.lock().upgrade() {
|
||||
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 {
|
||||
const NAME: &'static str = "Item";
|
||||
impl_aspect_for_item_aspect! {}
|
||||
}
|
||||
impl ItemAspect for Item {
|
||||
fn release(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||
@@ -129,7 +134,7 @@ impl Drop for Item {
|
||||
}
|
||||
|
||||
pub enum ItemType {
|
||||
Camera(CameraItem),
|
||||
Camera(Arc<CameraItem>),
|
||||
Panel(Arc<dyn PanelItemTrait>),
|
||||
}
|
||||
impl ItemType {
|
||||
@@ -284,8 +289,11 @@ impl ItemUI {
|
||||
.remove_aspect(acceptor.field.as_ref());
|
||||
}
|
||||
}
|
||||
impl AspectIdentifier for ItemUI {
|
||||
impl_aspect_for_item_ui_aspect_id! {}
|
||||
}
|
||||
impl Aspect for ItemUI {
|
||||
const NAME: &'static str = "Item";
|
||||
impl_aspect_for_item_ui_aspect! {}
|
||||
}
|
||||
impl Drop for ItemUI {
|
||||
fn drop(&mut self) {
|
||||
@@ -342,8 +350,11 @@ impl ItemAcceptor {
|
||||
let _ = item_acceptor_client::release_item(&node, alias.id);
|
||||
}
|
||||
}
|
||||
impl AspectIdentifier for ItemAcceptor {
|
||||
impl_aspect_for_item_acceptor_aspect_id! {}
|
||||
}
|
||||
impl Aspect for ItemAcceptor {
|
||||
const NAME: &'static str = "ItemAcceptor";
|
||||
impl_aspect_for_item_acceptor_aspect! {}
|
||||
}
|
||||
impl ItemAcceptorAspect for ItemAcceptor {}
|
||||
impl Drop for ItemAcceptor {
|
||||
@@ -364,6 +375,7 @@ pub fn register_item_ui_flex(
|
||||
) -> Result<()> {
|
||||
let ui = Node::from_id(&calling_client, type_info.ui_node_id, true).add_to_scenegraph()?;
|
||||
ItemUI::add_to(&ui, type_info)?;
|
||||
(type_info.add_ui_aspect)(&ui);
|
||||
Ok(())
|
||||
}
|
||||
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()?;
|
||||
Spatial::add_to(&node, Some(space.clone()), transform, false);
|
||||
ItemAcceptor::add_to(&node, type_info, field);
|
||||
(type_info.add_acceptor_aspect)(&node);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -391,6 +404,3 @@ fn acceptor_capture_item_flex(node: Arc<Node>, item: Arc<Node>) -> Result<()> {
|
||||
|
||||
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_ASPECT_ALIAS_INFO;
|
||||
use crate::nodes::{Aspect, AspectIdentifier};
|
||||
use crate::{
|
||||
core::{
|
||||
client::{get_env, state, Client, INTERNAL_CLIENT},
|
||||
client::{Client, INTERNAL_CLIENT, get_env, state},
|
||||
registry::Registry,
|
||||
},
|
||||
create_interface,
|
||||
nodes::{
|
||||
Node,
|
||||
drawable::model::ModelPart,
|
||||
items::{Item, ItemType, TypeInfo},
|
||||
spatial::{Spatial, Transform},
|
||||
Node,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Mat4;
|
||||
use lazy_static::lazy_static;
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use slotmap::{DefaultKey, Key, KeyData, SlotMap};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{debug, info};
|
||||
|
||||
@@ -32,6 +36,7 @@ impl Default for Geometry {
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref KEYMAPS: Mutex<SlotMap<DefaultKey, String>> = Mutex::new(SlotMap::default());
|
||||
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
||||
type_name: "panel",
|
||||
alias_info: PANEL_ITEM_ASPECT_ALIAS_INFO.clone(),
|
||||
@@ -39,6 +44,12 @@ lazy_static! {
|
||||
ui: Default::default(),
|
||||
items: 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| {
|
||||
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>>,
|
||||
);
|
||||
|
||||
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_move(&self, id: u32, position: Vector2<f32>);
|
||||
@@ -115,7 +126,7 @@ impl<B: Backend> PanelItem<B> {
|
||||
&ITEM_TYPE_INFO_PANEL,
|
||||
ItemType::Panel(generic_panel_item),
|
||||
);
|
||||
<Self as PanelItemAspect>::add_node_members(&node);
|
||||
node.add_aspect_raw(panel_item.clone());
|
||||
|
||||
(node, panel_item)
|
||||
}
|
||||
@@ -197,9 +208,12 @@ impl<B: Backend> PanelItem<B> {
|
||||
panel_item_client::destroy_child(&node, id);
|
||||
}
|
||||
}
|
||||
|
||||
// make these stupid vectors u32 in the protocol somehow!!!!!!!1
|
||||
|
||||
impl<B: Backend> AspectIdentifier for PanelItem<B> {
|
||||
impl_aspect_for_panel_item_aspect_id! {}
|
||||
}
|
||||
impl<B: Backend> Aspect for PanelItem<B> {
|
||||
impl_aspect_for_panel_item_aspect! {}
|
||||
}
|
||||
#[allow(unused)]
|
||||
impl<B: Backend> PanelItemAspect for PanelItem<B> {
|
||||
#[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)."]
|
||||
fn keyboard_keys(
|
||||
fn keyboard_key(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
surface: SurfaceId,
|
||||
keymap_id: u64,
|
||||
keys: Vec<i32>,
|
||||
key: u32,
|
||||
pressed: bool,
|
||||
) -> Result<()> {
|
||||
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||
return Ok(());
|
||||
};
|
||||
panel_item
|
||||
.backend()
|
||||
.keyboard_keys(&surface, keymap_id, keys);
|
||||
.keyboard_key(&surface, keymap_id, key, pressed);
|
||||
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<()> {
|
||||
super::acceptor_capture_item_flex(node, item)
|
||||
}
|
||||
@@ -435,22 +466,23 @@ impl<B: Backend> Drop for PanelItem<B> {
|
||||
}
|
||||
}
|
||||
|
||||
create_interface!(ItemInterface);
|
||||
impl InterfaceAspect for ItemInterface {
|
||||
impl InterfaceAspect for Interface {
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[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(
|
||||
_node: Arc<Node>,
|
||||
node: Arc<Node>,
|
||||
calling_client: Arc<Client>,
|
||||
id: u64,
|
||||
parent: Arc<Node>,
|
||||
transform: Transform,
|
||||
field: Arc<Node>,
|
||||
) -> Result<()> {
|
||||
node.add_aspect(PanelItemAcceptor);
|
||||
create_item_acceptor_flex(
|
||||
calling_client,
|
||||
id,
|
||||
@@ -460,4 +492,36 @@ impl InterfaceAspect for ItemInterface {
|
||||
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 audio;
|
||||
pub mod data;
|
||||
pub mod drawable;
|
||||
pub mod fields;
|
||||
pub mod input;
|
||||
@@ -10,13 +9,11 @@ pub mod spatial;
|
||||
|
||||
use self::alias::Alias;
|
||||
use crate::core::client::Client;
|
||||
use crate::core::error::{Result, ServerError};
|
||||
use crate::core::registry::Registry;
|
||||
use crate::core::scenegraph::MethodResponseSender;
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use parking_lot::Mutex;
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use dashmap::DashMap;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use spatial::Spatial;
|
||||
use stardust_xr::messenger::MessageSenderHandle;
|
||||
use stardust_xr::scenegraph::ScenegraphError;
|
||||
@@ -24,6 +21,7 @@ use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||
use std::any::{Any, TypeId};
|
||||
use std::fmt::Debug;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Weak};
|
||||
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!();
|
||||
|
||||
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>);
|
||||
impl Drop for OwnedNode {
|
||||
fn drop(&mut self) {
|
||||
@@ -64,8 +80,6 @@ pub struct Node {
|
||||
client: Weak<Client>,
|
||||
message_sender_handle: Option<MessageSenderHandle>,
|
||||
|
||||
local_signals: Mutex<FxHashMap<u64, Signal>>,
|
||||
local_methods: Mutex<FxHashMap<u64, Method>>,
|
||||
aliases: Registry<Alias>,
|
||||
aspects: Aspects,
|
||||
destroyable: bool,
|
||||
@@ -87,26 +101,24 @@ impl Node {
|
||||
client: Arc::downgrade(client),
|
||||
message_sender_handle: client.message_sender_handle.clone(),
|
||||
id,
|
||||
local_signals: Default::default(),
|
||||
local_methods: Default::default(),
|
||||
aliases: Default::default(),
|
||||
aspects: Default::default(),
|
||||
destroyable,
|
||||
};
|
||||
<Node as OwnedAspect>::add_node_members(&node);
|
||||
node.aspects.add(Owned);
|
||||
node
|
||||
}
|
||||
pub fn add_to_scenegraph(self) -> Result<Arc<Node>> {
|
||||
Ok(self
|
||||
.get_client()
|
||||
.ok_or_else(|| eyre!("Internal: Unable to get client"))?
|
||||
.ok_or(ServerError::NoClient)?
|
||||
.scenegraph
|
||||
.add_node(self))
|
||||
}
|
||||
pub fn add_to_scenegraph_owned(self) -> Result<OwnedNode> {
|
||||
Ok(OwnedNode(
|
||||
self.get_client()
|
||||
.ok_or_else(|| eyre!("Internal: Unable to get client"))?
|
||||
.ok_or(ServerError::NoClient)?
|
||||
.scenegraph
|
||||
.add_node(self),
|
||||
))
|
||||
@@ -118,7 +130,8 @@ impl Node {
|
||||
.global_transform()
|
||||
.to_scale_rotation_translation()
|
||||
.0
|
||||
.length_squared() > 0.0
|
||||
.length_squared()
|
||||
> 0.0
|
||||
} else {
|
||||
true
|
||||
}
|
||||
@@ -146,60 +159,57 @@ impl Node {
|
||||
// Ok(serialize(pid)?.into())
|
||||
// }
|
||||
|
||||
pub fn add_local_signal(&self, id: u64, signal: Signal) {
|
||||
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> {
|
||||
pub fn add_aspect<A: AspectIdentifier>(&self, aspect: A) -> Arc<A> {
|
||||
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)
|
||||
}
|
||||
pub fn get_aspect<A: Aspect>(&self) -> Result<Arc<A>> {
|
||||
pub fn get_aspect<A: AspectIdentifier>(&self) -> Result<Arc<A>> {
|
||||
self.aspects.get()
|
||||
}
|
||||
|
||||
pub fn send_local_signal(
|
||||
self: Arc<Self>,
|
||||
calling_client: Arc<Client>,
|
||||
aspect_id: u64,
|
||||
method: u64,
|
||||
message: Message,
|
||||
) -> Result<(), ScenegraphError> {
|
||||
if let Ok(alias) = self.get_aspect::<Alias>() {
|
||||
if !alias.info.server_signals.iter().any(|e| *e == method) {
|
||||
return Err(ScenegraphError::SignalNotFound);
|
||||
return Err(ScenegraphError::MemberNotFound);
|
||||
}
|
||||
alias
|
||||
.original
|
||||
.upgrade()
|
||||
.ok_or(ScenegraphError::BrokenAlias)?
|
||||
.send_local_signal(calling_client, method, message)
|
||||
.send_local_signal(calling_client, aspect_id, method, message)
|
||||
} else {
|
||||
let signal = self
|
||||
.local_signals
|
||||
.lock()
|
||||
.get(&method)
|
||||
.cloned()
|
||||
.ok_or(ScenegraphError::SignalNotFound)?;
|
||||
signal(self, calling_client, message).map_err(|error| ScenegraphError::SignalError {
|
||||
error: error.to_string(),
|
||||
})
|
||||
let aspect = self
|
||||
.aspects
|
||||
.0
|
||||
.get(&aspect_id)
|
||||
.ok_or(ScenegraphError::AspectNotFound)?
|
||||
.clone();
|
||||
aspect
|
||||
.run_signal(calling_client, self.clone(), method, message)
|
||||
.map_err(|error| ScenegraphError::MemberError {
|
||||
error: error.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn execute_local_method(
|
||||
self: Arc<Self>,
|
||||
calling_client: Arc<Client>,
|
||||
aspect_id: u64,
|
||||
method: u64,
|
||||
message: Message,
|
||||
response: MethodResponseSender,
|
||||
) {
|
||||
if let Ok(alias) = self.get_aspect::<Alias>() {
|
||||
if !alias.info.server_methods.iter().any(|e| *e == method) {
|
||||
response.send(Err(ScenegraphError::MethodNotFound));
|
||||
response.send(Err(ScenegraphError::MemberNotFound));
|
||||
return;
|
||||
}
|
||||
let Some(alias) = alias.original.upgrade() else {
|
||||
@@ -208,6 +218,7 @@ impl Node {
|
||||
};
|
||||
alias.execute_local_method(
|
||||
calling_client,
|
||||
aspect_id,
|
||||
method,
|
||||
Message {
|
||||
data: message.data.clone(),
|
||||
@@ -216,14 +227,19 @@ impl Node {
|
||||
response,
|
||||
)
|
||||
} else {
|
||||
let Some(method) = self.local_methods.lock().get(&method).cloned() else {
|
||||
response.send(Err(ScenegraphError::MethodNotFound));
|
||||
let Some(aspect) = self.aspects.0.get(&aspect_id).map(|v| v.clone()) else {
|
||||
response.send(Err(ScenegraphError::AspectNotFound));
|
||||
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();
|
||||
self.aliases
|
||||
.get_valid_contents()
|
||||
@@ -233,6 +249,7 @@ impl Node {
|
||||
.for_each(|node| {
|
||||
// Beware! file descriptors will not be sent to aliases!!!
|
||||
let _ = node.send_remote_signal(
|
||||
aspect_id,
|
||||
method,
|
||||
Message {
|
||||
data: message.data.clone(),
|
||||
@@ -241,12 +258,13 @@ impl Node {
|
||||
);
|
||||
});
|
||||
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(())
|
||||
}
|
||||
pub async fn execute_remote_method_typed<S: Serialize, D: DeserializeOwned>(
|
||||
&self,
|
||||
aspect_id: u64,
|
||||
method: u64,
|
||||
input: S,
|
||||
fds: Vec<OwnedFd>,
|
||||
@@ -254,13 +272,13 @@ impl Node {
|
||||
let message_sender_handle = self
|
||||
.message_sender_handle
|
||||
.as_ref()
|
||||
.ok_or(eyre!("Messenger does not exist for this node"))?;
|
||||
.ok_or(ServerError::NoMessenger)?;
|
||||
|
||||
let serialized = serialize(input)?;
|
||||
let result = message_sender_handle
|
||||
.method(self.id, method, &serialized, fds)?
|
||||
.await
|
||||
.map_err(|e| eyre!(e))?;
|
||||
.method(self.id, aspect_id, method, &serialized, fds)
|
||||
.await?
|
||||
.map_err(ServerError::RemoteMethodError)?;
|
||||
|
||||
let (message, fds) = result.into_components();
|
||||
let deserialized: D = deserialize(&message)?;
|
||||
@@ -271,61 +289,62 @@ impl Debug for Node {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Node")
|
||||
.field("id", &self.id)
|
||||
.field("local_signals", &self.local_signals.lock().keys())
|
||||
.field("local_methods", &self.local_methods.lock().keys())
|
||||
.field("destroyable", &self.destroyable)
|
||||
.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 {
|
||||
fn drop(&mut self) {
|
||||
// Debug breakpoint
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AspectIdentifier: Aspect {
|
||||
const ID: u64;
|
||||
}
|
||||
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)]
|
||||
struct Aspects(Mutex<FxHashMap<TypeId, Arc<dyn Any + Send + Sync + 'static>>>);
|
||||
|
||||
struct Aspects(DashMap<u64, Arc<dyn Aspect>>);
|
||||
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);
|
||||
self.add_raw(aspect.clone());
|
||||
aspect
|
||||
}
|
||||
fn add_raw<A: Aspect>(&self, aspect: Arc<A>) {
|
||||
self.0.lock().insert(Self::type_key::<A>(), aspect);
|
||||
fn add_raw<A: AspectIdentifier>(&self, aspect: Arc<A>) {
|
||||
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
|
||||
.lock()
|
||||
.get(&Self::type_key::<A>())
|
||||
.and_then(|a| Arc::downcast(a.clone()).ok())
|
||||
.ok_or(eyre!("Couldn't get aspect {}", A::NAME.to_lowercase()))
|
||||
}
|
||||
|
||||
fn type_key<A: 'static>() -> TypeId {
|
||||
TypeId::of::<A>()
|
||||
.get(&A::ID)
|
||||
// .cloned doesn't work for some reason
|
||||
.map(|v| v.clone())
|
||||
.map(|a| a.as_any())
|
||||
.and_then(|a| Arc::downcast(a).ok())
|
||||
.ok_or(ServerError::NoAspect(TypeId::of::<A>()))
|
||||
}
|
||||
}
|
||||
impl Drop for Aspects {
|
||||
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::Node;
|
||||
use crate::core::client::Client;
|
||||
use super::{Aspect, AspectIdentifier, Node};
|
||||
use crate::bail;
|
||||
use crate::core::client::{CLIENTS, Client};
|
||||
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::session::connection_env;
|
||||
use color_eyre::eyre::{bail, Result};
|
||||
use glam::Mat4;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
|
||||
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
||||
|
||||
stardust_xr_server_codegen::codegen_root_protocol!();
|
||||
|
||||
pub struct Root {
|
||||
@@ -23,18 +21,20 @@ pub struct Root {
|
||||
impl Root {
|
||||
pub fn create(client: &Arc<Client>, transform: Mat4) -> Result<Arc<Self>> {
|
||||
let node = Node::from_id(client, 0, false);
|
||||
<Self as RootAspect>::add_node_members(&node);
|
||||
let node = node.add_to_scenegraph()?;
|
||||
let _ = Spatial::add_to(&node, None, transform, false);
|
||||
|
||||
Ok(ROOT_REGISTRY.add(Root {
|
||||
node,
|
||||
let root_aspect = node.add_aspect(Root {
|
||||
node: node.clone(),
|
||||
connect_instant: Instant::now(),
|
||||
}))
|
||||
});
|
||||
Ok(root_aspect)
|
||||
}
|
||||
|
||||
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(
|
||||
&root.node,
|
||||
&FrameInfo {
|
||||
@@ -54,6 +54,12 @@ impl Root {
|
||||
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 {
|
||||
async fn get_state(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<ClientState> {
|
||||
let Some(state) = calling_client.state.get() else {
|
||||
@@ -92,13 +98,8 @@ impl RootAspect for Root {
|
||||
}
|
||||
|
||||
#[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(()));
|
||||
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 super::alias::Alias;
|
||||
use super::fields::{Field, FieldTrait};
|
||||
use super::Aspect;
|
||||
use super::{Aspect, AspectIdentifier};
|
||||
use crate::bail;
|
||||
use crate::core::client::Client;
|
||||
use crate::core::error::Result;
|
||||
use crate::core::registry::Registry;
|
||||
use crate::create_interface;
|
||||
use crate::nodes::{Node, OWNED_ASPECT_ALIAS_INFO};
|
||||
use color_eyre::eyre::{eyre, OptionExt, Result};
|
||||
use glam::{vec3a, Mat4, Quat, Vec3};
|
||||
use color_eyre::eyre::OptionExt;
|
||||
use glam::{Mat4, Quat, Vec3, vec3a};
|
||||
use mint::Vector3;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::ptr;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{Arc, OnceLock, Weak};
|
||||
use std::{f32, ptr};
|
||||
use stereokit_rust::maths::Bounds;
|
||||
|
||||
stardust_xr_server_codegen::codegen_spatial_protocol!();
|
||||
@@ -38,6 +38,12 @@ impl Transform {
|
||||
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! {
|
||||
pub static ref EXPORTED_SPATIALS: Mutex<FxHashMap<u64, Arc<Node>>> = Mutex::new(FxHashMap::default());
|
||||
@@ -52,7 +58,7 @@ pub struct Spatial {
|
||||
transform: Mutex<Mat4>,
|
||||
zone: Mutex<Weak<Zone>>,
|
||||
children: Registry<Spatial>,
|
||||
pub bounding_box_calc: OnceCell<fn(&Node) -> Bounds>,
|
||||
pub bounding_box_calc: OnceLock<fn(&Node) -> Bounds>,
|
||||
}
|
||||
|
||||
impl Spatial {
|
||||
@@ -64,7 +70,7 @@ impl Spatial {
|
||||
transform: Mutex::new(transform),
|
||||
zone: Mutex::new(Weak::new()),
|
||||
children: Registry::new(),
|
||||
bounding_box_calc: OnceCell::default(),
|
||||
bounding_box_calc: OnceLock::default(),
|
||||
})
|
||||
}
|
||||
pub fn add_to(
|
||||
@@ -74,16 +80,14 @@ impl Spatial {
|
||||
zoneable: bool,
|
||||
) -> Arc<Spatial> {
|
||||
let spatial = Spatial::new(Arc::downgrade(node), parent.clone(), transform);
|
||||
<Spatial as SpatialAspect>::add_node_members(node);
|
||||
if zoneable {
|
||||
ZONEABLE_REGISTRY.add_raw(&spatial);
|
||||
}
|
||||
if let Some(parent) = parent {
|
||||
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(SpatialRef);
|
||||
spatial
|
||||
}
|
||||
|
||||
@@ -205,7 +209,7 @@ impl Spatial {
|
||||
.map(|parent| self.is_ancestor_of((*parent).clone()))
|
||||
.unwrap_or(false);
|
||||
if is_ancestor {
|
||||
return Err(eyre!("Setting spatial parent would cause a loop"));
|
||||
bail!("Setting spatial parent would cause a loop");
|
||||
}
|
||||
self.set_parent(parent);
|
||||
|
||||
@@ -220,7 +224,7 @@ impl Spatial {
|
||||
.map(|parent| self.is_ancestor_of((*parent).clone()))
|
||||
.unwrap_or(false);
|
||||
if is_ancestor {
|
||||
return Err(eyre!("Setting spatial parent would cause a loop"));
|
||||
bail!("Setting spatial parent would cause a loop");
|
||||
}
|
||||
|
||||
self.set_local_transform(Spatial::space_to_space_matrix(
|
||||
@@ -238,70 +242,14 @@ impl Spatial {
|
||||
.upgrade()
|
||||
.map(|zone| zone.field.clone())
|
||||
.map(|field| field.distance(self, vec3a(0.0, 0.0, 0.0)))
|
||||
.unwrap_or(f32::MAX)
|
||||
.unwrap_or(f32::NEG_INFINITY)
|
||||
}
|
||||
}
|
||||
impl AspectIdentifier for Spatial {
|
||||
impl_aspect_for_spatial_aspect_id! {}
|
||||
}
|
||||
impl Aspect for Spatial {
|
||||
const NAME: &'static str = "Spatial";
|
||||
}
|
||||
impl SpatialRefAspect for Spatial {
|
||||
async fn get_local_bounding_box(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
) -> Result<BoundingBox> {
|
||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||
let bounds = this_spatial.get_bounding_box();
|
||||
|
||||
Ok(BoundingBox {
|
||||
center: Vec3::from(bounds.center).into(),
|
||||
size: Vec3::from(bounds.dimensions).into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_relative_bounding_box(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
relative_to: Arc<Node>,
|
||||
) -> Result<BoundingBox> {
|
||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
||||
let center = Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial))
|
||||
.transform_point3([0.0; 3].into());
|
||||
let mut bounds = Bounds {
|
||||
center: center.into(),
|
||||
dimensions: [0.0; 3].into(),
|
||||
};
|
||||
bounds.grown_box(
|
||||
this_spatial.get_bounding_box(),
|
||||
Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial)),
|
||||
);
|
||||
|
||||
Ok(BoundingBox {
|
||||
center: Vec3::from(bounds.center).into(),
|
||||
size: Vec3::from(bounds.dimensions).into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_transform(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
relative_to: Arc<Node>,
|
||||
) -> Result<Transform> {
|
||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
||||
|
||||
let (scale, rotation, position) = Spatial::space_to_space_matrix(
|
||||
Some(this_spatial.as_ref()),
|
||||
Some(relative_spatial.as_ref()),
|
||||
)
|
||||
.to_scale_rotation_translation();
|
||||
|
||||
Ok(Transform {
|
||||
translation: Some(position.into()),
|
||||
rotation: Some(rotation.into()),
|
||||
scale: Some(scale.into()),
|
||||
})
|
||||
}
|
||||
impl_aspect_for_spatial_aspect! {}
|
||||
}
|
||||
impl SpatialAspect for Spatial {
|
||||
fn set_local_transform(
|
||||
@@ -389,6 +337,73 @@ impl Drop for Spatial {
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
) -> Result<BoundingBox> {
|
||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||
let bounds = this_spatial.get_bounding_box();
|
||||
|
||||
Ok(BoundingBox {
|
||||
center: Vec3::from(bounds.center).into(),
|
||||
size: Vec3::from(bounds.dimensions).into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_relative_bounding_box(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
relative_to: Arc<Node>,
|
||||
) -> Result<BoundingBox> {
|
||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
||||
let center = Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial))
|
||||
.transform_point3([0.0; 3].into());
|
||||
let mut bounds = Bounds {
|
||||
center: center.into(),
|
||||
dimensions: [0.0; 3].into(),
|
||||
};
|
||||
bounds.grown_box(
|
||||
this_spatial.get_bounding_box(),
|
||||
Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial)),
|
||||
);
|
||||
|
||||
Ok(BoundingBox {
|
||||
center: Vec3::from(bounds.center).into(),
|
||||
size: Vec3::from(bounds.dimensions).into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_transform(
|
||||
node: Arc<Node>,
|
||||
_calling_client: Arc<Client>,
|
||||
relative_to: Arc<Node>,
|
||||
) -> Result<Transform> {
|
||||
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
||||
|
||||
let (scale, rotation, position) = Spatial::space_to_space_matrix(
|
||||
Some(this_spatial.as_ref()),
|
||||
Some(relative_spatial.as_ref()),
|
||||
)
|
||||
.to_scale_rotation_translation();
|
||||
|
||||
Ok(Transform {
|
||||
translation: Some(position.into()),
|
||||
rotation: Some(rotation.into()),
|
||||
scale: Some(scale.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_transform(transform: Transform, position: bool, rotation: bool, scale: bool) -> Mat4 {
|
||||
let position = position
|
||||
.then_some(transform.translation)
|
||||
@@ -406,8 +421,7 @@ pub fn parse_transform(transform: Transform, position: bool, rotation: bool, sca
|
||||
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
||||
}
|
||||
|
||||
pub struct SpatialInterface;
|
||||
impl InterfaceAspect for SpatialInterface {
|
||||
impl InterfaceAspect for Interface {
|
||||
fn create_spatial(
|
||||
_node: Arc<Node>,
|
||||
calling_client: Arc<Client>,
|
||||
@@ -445,7 +459,7 @@ impl InterfaceAspect for SpatialInterface {
|
||||
calling_client: Arc<Client>,
|
||||
uid: u64,
|
||||
) -> Result<Arc<Node>> {
|
||||
EXPORTED_SPATIALS
|
||||
Ok(EXPORTED_SPATIALS
|
||||
.lock()
|
||||
.get(&uid)
|
||||
.map(|s| {
|
||||
@@ -457,8 +471,6 @@ impl InterfaceAspect for SpatialInterface {
|
||||
)
|
||||
.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::{
|
||||
Spatial, ZoneAspect, SPATIAL_ASPECT_ALIAS_INFO, SPATIAL_REF_ASPECT_ALIAS_INFO,
|
||||
ZONEABLE_REGISTRY,
|
||||
SPATIAL_ASPECT_ALIAS_INFO, SPATIAL_REF_ASPECT_ALIAS_INFO, Spatial, ZONEABLE_REGISTRY,
|
||||
ZoneAspect,
|
||||
};
|
||||
use crate::{
|
||||
core::{client::Client, registry::Registry},
|
||||
core::{client::Client, error::Result, registry::Registry},
|
||||
nodes::{
|
||||
alias::{get_original, Alias, AliasList},
|
||||
Node,
|
||||
alias::{Alias, AliasList, get_original},
|
||||
fields::{Field, FieldTrait},
|
||||
Aspect, Node,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::vec3a;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
|
||||
let old_distance = spatial.zone_distance();
|
||||
let new_distance = zone.field.distance(spatial, vec3a(0.0, 0.0, 0.0));
|
||||
if new_distance.abs() < old_distance.abs() {
|
||||
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);
|
||||
if new_distance.abs() > old_distance.abs() {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
let Some(spatial_node) = spatial.node.upgrade() else {
|
||||
@@ -73,7 +74,6 @@ impl Zone {
|
||||
intersecting: AliasList::default(),
|
||||
captured: AliasList::default(),
|
||||
});
|
||||
<Zone as ZoneAspect>::add_node_members(node);
|
||||
node.add_aspect_raw(zone.clone());
|
||||
zone
|
||||
}
|
||||
@@ -82,15 +82,17 @@ impl Zone {
|
||||
|
||||
let current_zoneables = Registry::new();
|
||||
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());
|
||||
if distance > 0.0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(zone) = zoneable.zone.lock().upgrade() {
|
||||
let zoneable_distance = zone.field.distance(&zoneable, [0.0; 3].into());
|
||||
if zoneable_distance < distance {
|
||||
continue;
|
||||
}
|
||||
if distance < zoneable.zone_distance() {
|
||||
continue;
|
||||
}
|
||||
current_zoneables.add_raw(&zoneable);
|
||||
}
|
||||
@@ -124,9 +126,6 @@ impl Zone {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Aspect for Zone {
|
||||
const NAME: &'static str = "Zone";
|
||||
}
|
||||
impl ZoneAspect for Zone {
|
||||
fn update(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||
let zone = node.get_aspect::<Zone>()?;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::{
|
||||
core::client::INTERNAL_CLIENT,
|
||||
nodes::{
|
||||
fields::{FieldTrait, Ray},
|
||||
input::{InputDataType, InputMethod, Pointer, INPUT_HANDLER_REGISTRY},
|
||||
spatial::Spatial,
|
||||
Node, OwnedNode,
|
||||
fields::{FieldTrait, Ray},
|
||||
input::{INPUT_HANDLER_REGISTRY, InputDataType, InputMethod, Pointer},
|
||||
spatial::Spatial,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::{vec3, Mat4};
|
||||
use glam::{Mat4, vec3};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stardust_xr::values::Datamap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -5,40 +5,43 @@ pub mod sk_hand;
|
||||
|
||||
use crate::nodes::{
|
||||
fields::{Field, FieldTrait, Ray},
|
||||
input::{InputDataTrait, InputDataType, InputHandler, InputMethod, INPUT_HANDLER_REGISTRY},
|
||||
input::{INPUT_HANDLER_REGISTRY, InputDataTrait, InputDataType, InputHandler, InputMethod},
|
||||
spatial::Spatial,
|
||||
};
|
||||
use glam::vec3;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CaptureManager {
|
||||
pub capture: Option<Arc<InputHandler>>,
|
||||
pub capture: Weak<InputHandler>,
|
||||
}
|
||||
impl CaptureManager {
|
||||
pub fn update_capture(&mut self, pointer: &InputMethod) {
|
||||
if let Some(capture) = &self.capture {
|
||||
if !pointer
|
||||
.internal_capture_requests
|
||||
pub fn update_capture(&mut self, method: &InputMethod) {
|
||||
if let Some(capture) = &self.capture.upgrade() {
|
||||
if !method
|
||||
.capture_attempts
|
||||
.get_valid_contents()
|
||||
.contains(capture)
|
||||
{
|
||||
self.capture.take();
|
||||
self.capture = Weak::new();
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn set_new_capture(
|
||||
&mut self,
|
||||
pointer: &InputMethod,
|
||||
method: &InputMethod,
|
||||
distance_calculator: DistanceCalculator,
|
||||
) {
|
||||
if self.capture.is_none() {
|
||||
self.capture = find_closest_capture(pointer, distance_calculator);
|
||||
if self.capture.upgrade().is_none() {
|
||||
self.capture = find_closest_capture(method, distance_calculator);
|
||||
}
|
||||
}
|
||||
pub fn apply_capture(&self, method: &InputMethod) {
|
||||
method.captures.clear();
|
||||
if let Some(capture) = &self.capture {
|
||||
if let Some(capture) = &self.capture.upgrade() {
|
||||
method.set_handler_order([capture].into_iter());
|
||||
method.captures.add_raw(capture);
|
||||
}
|
||||
@@ -50,9 +53,9 @@ type DistanceCalculator = fn(&Arc<Spatial>, &InputDataType, &Field) -> Option<f3
|
||||
pub fn find_closest_capture(
|
||||
method: &InputMethod,
|
||||
distance_calculator: DistanceCalculator,
|
||||
) -> Option<Arc<InputHandler>> {
|
||||
) -> Weak<InputHandler> {
|
||||
method
|
||||
.internal_capture_requests
|
||||
.capture_attempts
|
||||
.get_valid_contents()
|
||||
.into_iter()
|
||||
.filter_map(|h| {
|
||||
@@ -60,39 +63,31 @@ pub fn find_closest_capture(
|
||||
.map(|dist| (h.clone(), dist))
|
||||
})
|
||||
.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(
|
||||
method: &InputMethod,
|
||||
distance_calculator: DistanceCalculator,
|
||||
) -> Vec<Arc<InputHandler>> {
|
||||
INPUT_HANDLER_REGISTRY
|
||||
) -> Vec<(Arc<InputHandler>, f32)> {
|
||||
let mut handlers = INPUT_HANDLER_REGISTRY
|
||||
.get_valid_contents()
|
||||
.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| {
|
||||
handler
|
||||
.field
|
||||
.spatial
|
||||
.node()
|
||||
.map_or(false, |node| node.enabled())
|
||||
.is_some_and(|node| node.enabled())
|
||||
})
|
||||
.filter_map(|handler| {
|
||||
distance_calculator(&method.spatial, &method.data.lock(), &handler.field)
|
||||
.map(|distance| (vec![handler], distance))
|
||||
.map(|distance| (handler, distance))
|
||||
})
|
||||
.filter(|(_, distance)| *distance > 0.0)
|
||||
.reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| {
|
||||
if (distance_a - distance_b).abs() < 0.001 {
|
||||
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()
|
||||
.collect::<Vec<_>>();
|
||||
handlers.sort_by(|(_, dist_a), (_, dist_b)| dist_a.partial_cmp(dist_b).unwrap());
|
||||
handlers
|
||||
}
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
use super::{CaptureManager, DistanceCalculator, get_sorted_handlers};
|
||||
use crate::{
|
||||
core::client::INTERNAL_CLIENT,
|
||||
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,
|
||||
fields::{EXPORTED_FIELDS, Field, FieldTrait, Ray},
|
||||
input::{InputDataType, InputMethod, Pointer},
|
||||
items::panel::KEYMAPS,
|
||||
spatial::Spatial,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::{vec3, Mat4, Vec3};
|
||||
use glam::{Mat4, Vec3, vec3};
|
||||
use mint::Vector2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
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 stereokit_rust::system::{Input, Key};
|
||||
use xkbcommon_rs::{xkb_keymap::CompileFlags, Context, Keymap, KeymapFormat};
|
||||
|
||||
use super::{get_sorted_handlers, CaptureManager, DistanceCalculator};
|
||||
use tokio::task::JoinSet;
|
||||
use tokio::time::{Duration, timeout};
|
||||
use xkbcommon_rs::{Context, Keymap, KeymapFormat, xkb_keymap::CompileFlags};
|
||||
use zbus::{Connection, names::OwnedInterfaceName};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct MouseEvent {
|
||||
@@ -46,12 +49,14 @@ impl Default for MouseEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||
pub struct KeyboardEvent {
|
||||
pub keyboard: (),
|
||||
pub xkbv1: (),
|
||||
pub keymap_id: u64,
|
||||
pub keys: Vec<i32>,
|
||||
#[zbus::proxy(
|
||||
interface = "org.stardustxr.XKBv1",
|
||||
default_service = "org.stardustxr.XKBv1"
|
||||
)]
|
||||
trait KeyboardHandler {
|
||||
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)]
|
||||
@@ -62,8 +67,6 @@ pub struct MousePointer {
|
||||
pointer: Arc<InputMethod>,
|
||||
capture_manager: CaptureManager,
|
||||
mouse_datamap: MouseEvent,
|
||||
keyboard_datamap: KeyboardEvent,
|
||||
keyboard_sender: Arc<PulseSender>,
|
||||
}
|
||||
impl MousePointer {
|
||||
pub fn new() -> Result<Self> {
|
||||
@@ -83,29 +86,16 @@ impl MousePointer {
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let keyboard_sender = PulseSender::add_to(
|
||||
&node.0,
|
||||
Datamap::from_typed(KeyboardEvent::default()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(MousePointer {
|
||||
node,
|
||||
spatial,
|
||||
pointer,
|
||||
capture_manager: CaptureManager::default(),
|
||||
mouse_datamap: Default::default(),
|
||||
keyboard_datamap: KeyboardEvent {
|
||||
keyboard: (),
|
||||
xkbv1: (),
|
||||
keymap_id: keymap.data().as_ffi(),
|
||||
keys: vec![],
|
||||
},
|
||||
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 ray = mouse.get_ray();
|
||||
@@ -123,7 +113,9 @@ impl MousePointer {
|
||||
select: Input::key(Key::MouseLeft).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,
|
||||
grab: Input::key(Key::MouseBack).is_active() as u32 as f32,
|
||||
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_discrete: [0.0, mouse.scroll_change / 120.0].into(),
|
||||
raw_input_events: vec![],
|
||||
@@ -131,7 +123,7 @@ impl MousePointer {
|
||||
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
|
||||
}
|
||||
self.target_pointer_input();
|
||||
self.send_keyboard_input();
|
||||
self.send_keyboard_input(dbus_connection, object_registry);
|
||||
}
|
||||
fn target_pointer_input(&mut self) {
|
||||
let distance_calculator: DistanceCalculator = |space, data, field| {
|
||||
@@ -150,64 +142,115 @@ impl MousePointer {
|
||||
.set_new_capture(&self.pointer, distance_calculator);
|
||||
self.capture_manager.apply_capture(&self.pointer);
|
||||
|
||||
if self.capture_manager.capture.is_some() {
|
||||
if self.capture_manager.capture.upgrade().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let sorted_handlers = get_sorted_handlers(&self.pointer, distance_calculator);
|
||||
self.pointer.set_handler_order(sorted_handlers.iter());
|
||||
let mut handlers = get_sorted_handlers(&self.pointer, distance_calculator);
|
||||
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) {
|
||||
let rx = PULSE_RECEIVER_REGISTRY
|
||||
.get_valid_contents()
|
||||
.into_iter()
|
||||
.filter(|rx| mask_matches(&rx.mask, &self.keyboard_sender.mask))
|
||||
.map(|rx| {
|
||||
let result = rx.field.ray_march(Ray {
|
||||
origin: vec3(0.0, 0.0, 0.0),
|
||||
direction: vec3(0.0, 0.0, -1.0),
|
||||
space: self.spatial.clone(),
|
||||
});
|
||||
(rx, result)
|
||||
})
|
||||
.filter(|(_rx, result)| {
|
||||
result.deepest_point_distance > 0.0 && result.min_distance < 0.05
|
||||
})
|
||||
.reduce(|(rx_a, result_a), (rx_b, result_b)| {
|
||||
if result_a.deepest_point_distance < result_b.deepest_point_distance {
|
||||
(rx_a, result_a)
|
||||
} else {
|
||||
(rx_b, result_b)
|
||||
pub fn send_keyboard_input(
|
||||
&mut self,
|
||||
dbus_connection: &Connection,
|
||||
object_registry: &ObjectRegistry,
|
||||
) {
|
||||
let keyboard_handlers = object_registry.get_objects("org.stardustxr.XKBv1");
|
||||
|
||||
// Spawn async task to handle keyboard input
|
||||
tokio::spawn({
|
||||
let keyboard_handlers = keyboard_handlers.clone();
|
||||
let spatial = self.spatial.clone();
|
||||
let keymap_id = self.keymap.data().as_ffi();
|
||||
let dbus_connection = dbus_connection.clone();
|
||||
|
||||
async move {
|
||||
let mut closest_handler = None;
|
||||
let mut closest_distance = f32::MAX;
|
||||
|
||||
let mut join_set = JoinSet::new();
|
||||
for handler in &keyboard_handlers {
|
||||
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()
|
||||
});
|
||||
}
|
||||
})
|
||||
.map(|(rx, _)| rx);
|
||||
while let Some(Ok(Some((handler, field_ref_id)))) = join_set.join_next().await {
|
||||
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 keys = (8_u32..254)
|
||||
.map(|i| unsafe { std::mem::transmute(i) })
|
||||
.filter_map(|k| Some((map_key(k)?, Input::key(k))))
|
||||
.filter_map(|(i, k)| {
|
||||
if k.is_just_active() {
|
||||
Some(i as i32)
|
||||
} else if k.is_just_inactive() {
|
||||
Some(-(i as i32))
|
||||
} else {
|
||||
None
|
||||
let result = field_ref.ray_march(Ray {
|
||||
origin: vec3(0.0, 0.0, 0.0),
|
||||
direction: vec3(0.0, 0.0, -1.0),
|
||||
space: spatial.clone(),
|
||||
});
|
||||
|
||||
if result.deepest_point_distance > 0.0
|
||||
&& result.min_distance < 0.05
|
||||
&& result.deepest_point_distance < closest_distance
|
||||
{
|
||||
closest_distance = result.deepest_point_distance;
|
||||
closest_handler = Some(handler);
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
self.keyboard_datamap.keys = keys;
|
||||
if !self.keyboard_datamap.keys.is_empty() {
|
||||
pulse_receiver_client::data(
|
||||
&rx.node.upgrade().unwrap(),
|
||||
&self.node.0,
|
||||
&Datamap::from_typed(&self.keyboard_datamap).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let Some(handler) = closest_handler else {
|
||||
return;
|
||||
};
|
||||
let Ok(keyboard_handler) = handler
|
||||
.to_typed_proxy::<KeyboardHandlerProxy>(&dbus_connection)
|
||||
.await
|
||||
else {
|
||||
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::{
|
||||
core::client::INTERNAL_CLIENT,
|
||||
nodes::{
|
||||
fields::{Field, FieldTrait},
|
||||
input::{InputDataType, InputHandler, InputMethod, Tip, INPUT_HANDLER_REGISTRY},
|
||||
spatial::Spatial,
|
||||
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 glam::{Mat4, Vec2, Vec3};
|
||||
@@ -40,18 +40,19 @@ pub struct SkController {
|
||||
material: Material,
|
||||
capture_manager: CaptureManager,
|
||||
datamap: ControllerDatamap,
|
||||
tracked: ObjectHandle<Tracked>,
|
||||
}
|
||||
impl SkController {
|
||||
pub fn new(connection: &Connection, handed: Handed) -> Result<Self> {
|
||||
let (spatial, object_handle) = SpatialRef::create(
|
||||
connection,
|
||||
&("/org/stardustxr/Controller/".to_string()
|
||||
+ match handed {
|
||||
Handed::Left => "left",
|
||||
_ => "right",
|
||||
}),
|
||||
);
|
||||
let model = Model::copy(Model::from_memory(
|
||||
Input::set_controller_model(handed, Some(Model::new()));
|
||||
let path = "/org/stardustxr/Controller/".to_string()
|
||||
+ match handed {
|
||||
Handed::Left => "left",
|
||||
_ => "right",
|
||||
};
|
||||
let (spatial, object_handle) = SpatialRef::create(connection, &path);
|
||||
let tracked = Tracked::new(connection, &path);
|
||||
let model = Model::copy(&Model::from_memory(
|
||||
"cursor.glb",
|
||||
include_bytes!("cursor.glb"),
|
||||
None,
|
||||
@@ -74,19 +75,28 @@ impl SkController {
|
||||
material,
|
||||
capture_manager: CaptureManager::default(),
|
||||
datamap: Default::default(),
|
||||
tracked,
|
||||
})
|
||||
}
|
||||
pub fn update(&mut self, token: &MainThreadToken) {
|
||||
let controller = Input::controller(self.handed);
|
||||
let input_node = self.input.spatial.node().unwrap();
|
||||
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(
|
||||
controller.aim.orientation.into(),
|
||||
controller.aim.position.into(),
|
||||
);
|
||||
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)
|
||||
} else {
|
||||
Color128::new_rgb(0.0, 1.0, 0.75)
|
||||
@@ -118,11 +128,12 @@ impl SkController {
|
||||
.set_new_capture(&self.input, distance_calculator);
|
||||
self.capture_manager.apply_capture(&self.input);
|
||||
|
||||
if self.capture_manager.capture.is_some() {
|
||||
if self.capture_manager.capture.upgrade().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
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::nodes::fields::{Field, FieldTrait};
|
||||
use crate::nodes::input::{InputDataType, InputHandler, INPUT_HANDLER_REGISTRY};
|
||||
use crate::nodes::OwnedNode;
|
||||
use crate::nodes::fields::{Field, FieldTrait};
|
||||
use crate::nodes::input::{INPUT_HANDLER_REGISTRY, InputDataType, InputHandler};
|
||||
use crate::nodes::{
|
||||
Node,
|
||||
input::{Hand, InputMethod, Joint},
|
||||
spatial::Spatial,
|
||||
Node,
|
||||
};
|
||||
use crate::objects::{ObjectHandle, SpatialRef};
|
||||
use crate::objects::{ObjectHandle, SpatialRef, Tracked};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::{Mat4, Quat, Vec3};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stardust_xr::values::Datamap;
|
||||
use std::f32::INFINITY;
|
||||
use std::sync::Arc;
|
||||
use stereokit_rust::material::Material;
|
||||
use stereokit_rust::sk::{DisplayMode, MainThreadToken, Sk};
|
||||
use stereokit_rust::system::{HandJoint, HandSource, Handed, Input, LinePoint, Lines};
|
||||
use stereokit_rust::util::Color128;
|
||||
use zbus::Connection;
|
||||
|
||||
use super::{get_sorted_handlers, CaptureManager};
|
||||
use super::{CaptureManager, get_sorted_handlers};
|
||||
|
||||
fn convert_joint(joint: HandJoint) -> Joint {
|
||||
Joint {
|
||||
@@ -44,6 +44,7 @@ pub struct SkHand {
|
||||
input: Arc<InputMethod>,
|
||||
capture_manager: CaptureManager,
|
||||
datamap: HandDatamap,
|
||||
tracked: ObjectHandle<Tracked>,
|
||||
}
|
||||
impl SkHand {
|
||||
pub fn new(connection: &Connection, handed: Handed) -> Result<Self> {
|
||||
@@ -55,6 +56,14 @@ impl SkHand {
|
||||
_ => "right",
|
||||
} + "/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()?;
|
||||
Spatial::add_to(&_node.0, None, Mat4::IDENTITY, false);
|
||||
let hand = InputDataType::Hand(Hand {
|
||||
@@ -63,19 +72,20 @@ impl SkHand {
|
||||
});
|
||||
let datamap = Datamap::from_typed(HandDatamap::default())?;
|
||||
let input = InputMethod::add_to(&_node.0, hand, datamap)?;
|
||||
Input::hand_visible(handed, true);
|
||||
|
||||
Input::hand_visible(handed, false);
|
||||
Ok(SkHand {
|
||||
_node,
|
||||
palm_spatial,
|
||||
palm_object,
|
||||
handed,
|
||||
input,
|
||||
tracked,
|
||||
capture_manager: CaptureManager::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 real_hand = Input::hand_source(self.handed) as u32 == HandSource::Articulated as u32;
|
||||
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)
|
||||
&& 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.distal = convert_joint(sk_hand.fingers[0][3]);
|
||||
hand.thumb.proximal = convert_joint(sk_hand.fingers[0][2]);
|
||||
@@ -122,15 +140,12 @@ impl SkHand {
|
||||
|
||||
hand.elbow = None;
|
||||
|
||||
self.draw(
|
||||
token,
|
||||
if self.capture_manager.capture.is_none() {
|
||||
Color128::new_rgb(1.0, 1.0, 1.0)
|
||||
} else {
|
||||
Color128::new_rgb(0.0, 1.0, 0.75)
|
||||
},
|
||||
hand,
|
||||
);
|
||||
let hand_color = if self.capture_manager.capture.upgrade().is_none() {
|
||||
Color128::new_rgb(1.0, 1.0, 1.0)
|
||||
} else {
|
||||
Color128::new_rgb(0.0, 1.0, 0.75)
|
||||
};
|
||||
material.color_tint(hand_color);
|
||||
}
|
||||
}
|
||||
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
||||
@@ -159,72 +174,18 @@ impl SkHand {
|
||||
.set_new_capture(&self.input, distance_calculator);
|
||||
self.capture_manager.apply_capture(&self.input);
|
||||
|
||||
if self.capture_manager.capture.is_some() {
|
||||
if self.capture_manager.capture.upgrade().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
// thumb
|
||||
Lines::add_list(
|
||||
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),
|
||||
],
|
||||
);
|
||||
}
|
||||
impl Drop for SkHand {
|
||||
fn drop(&mut self) {
|
||||
Input::hand_visible(self.handed, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,24 +3,29 @@
|
||||
use crate::{
|
||||
core::client::INTERNAL_CLIENT,
|
||||
nodes::{
|
||||
fields::{Field, Shape, EXPORTED_FIELDS},
|
||||
spatial::{Spatial, EXPORTED_SPATIALS},
|
||||
Node, OwnedNode,
|
||||
fields::{EXPORTED_FIELDS, Field, Shape},
|
||||
spatial::{EXPORTED_SPATIALS, Spatial},
|
||||
},
|
||||
};
|
||||
use glam::{vec3, Mat4};
|
||||
use glam::{Mat4, vec3};
|
||||
use input::{
|
||||
eye_pointer::EyePointer, mouse_pointer::MousePointer, sk_controller::SkController,
|
||||
sk_hand::SkHand,
|
||||
};
|
||||
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::{
|
||||
material::Material,
|
||||
sk::{DisplayMode, MainThreadToken, Sk},
|
||||
system::{Handed, Input, Key, World},
|
||||
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 play_space;
|
||||
@@ -45,6 +50,7 @@ pub struct ServerObjects {
|
||||
connection: Connection,
|
||||
hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>),
|
||||
play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>,
|
||||
hand_materials: [Material; 2],
|
||||
inputs: Inputs,
|
||||
disable_controllers: bool,
|
||||
disable_hands: bool,
|
||||
@@ -53,15 +59,13 @@ impl ServerObjects {
|
||||
pub fn new(
|
||||
connection: Connection,
|
||||
sk: &Sk,
|
||||
hand_materials: [Material; 2],
|
||||
disable_controllers: bool,
|
||||
disable_hands: bool,
|
||||
) -> ServerObjects {
|
||||
let hmd = SpatialRef::create(&connection, "/org/stardustxr/HMD");
|
||||
|
||||
let play_space = (World::has_bounds()
|
||||
&& World::get_bounds_size().x != 0.0
|
||||
&& World::get_bounds_size().y != 0.0)
|
||||
.then(|| SpatialRef::create(&connection, "/org/stardustxr/PlaySpace"));
|
||||
let play_space = Some(SpatialRef::create(&connection, "/org/stardustxr/PlaySpace"));
|
||||
if play_space.is_some() {
|
||||
let dbus_connection = connection.clone();
|
||||
tokio::task::spawn(async move {
|
||||
@@ -106,13 +110,20 @@ impl ServerObjects {
|
||||
connection,
|
||||
hmd,
|
||||
play_space,
|
||||
hand_materials,
|
||||
inputs,
|
||||
disable_controllers,
|
||||
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();
|
||||
self.hmd
|
||||
.0
|
||||
@@ -132,6 +143,7 @@ impl ServerObjects {
|
||||
));
|
||||
}
|
||||
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if sk.get_active_display_mode() != DisplayMode::MixedReality {
|
||||
if Input::key(Key::F6).is_just_inactive() {
|
||||
self.inputs = Inputs::MousePointer(MousePointer::new().unwrap());
|
||||
@@ -142,12 +154,12 @@ impl ServerObjects {
|
||||
// SkController::new(Handed::Right).unwrap(),
|
||||
// ));
|
||||
// }
|
||||
if Input::key(Key::F8).is_just_inactive() {
|
||||
self.inputs = Inputs::Hands {
|
||||
left: SkHand::new(&self.connection, Handed::Left).unwrap(),
|
||||
right: SkHand::new(&self.connection, Handed::Right).unwrap(),
|
||||
};
|
||||
}
|
||||
// if Input::key(Key::F8).is_just_inactive() {
|
||||
// self.inputs = Inputs::Hands {
|
||||
// left: SkHand::new(&self.connection, Handed::Left).unwrap(),
|
||||
// right: SkHand::new(&self.connection, Handed::Right).unwrap(),
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
match &mut self.inputs {
|
||||
@@ -162,28 +174,38 @@ impl ServerObjects {
|
||||
controller_left.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 {
|
||||
hand_left.update(sk, token);
|
||||
hand_right.update(sk, token);
|
||||
hand_left.update(sk, token, &mut self.hand_materials[0]);
|
||||
hand_right.update(sk, token, &mut self.hand_materials[1]);
|
||||
}
|
||||
if let Some(eye_pointer) = eye_pointer {
|
||||
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)) => {
|
||||
// left.update(token);
|
||||
// right.update(token);
|
||||
// }
|
||||
Inputs::Hands { left, right } => {
|
||||
left.update(sk, token);
|
||||
right.update(sk, token);
|
||||
left.update(sk, token, &mut self.hand_materials[0]);
|
||||
right.update(sk, token, &mut self.hand_materials[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
fn drop(&mut self) {
|
||||
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);
|
||||
impl FieldRef {
|
||||
pub fn create(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use stereokit_rust::system::World;
|
||||
use zbus::{interface, Connection, ObjectServer};
|
||||
use zbus::{Connection, ObjectServer, interface};
|
||||
|
||||
pub struct PlaySpaceBounds;
|
||||
impl PlaySpaceBounds {
|
||||
@@ -15,12 +15,19 @@ impl PlaySpaceBounds {
|
||||
impl PlaySpaceBounds {
|
||||
#[zbus(property)]
|
||||
fn bounds(&self) -> Vec<(f64, f64)> {
|
||||
let bounds = World::get_bounds_size();
|
||||
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()),
|
||||
]
|
||||
if (World::has_bounds()
|
||||
&& World::get_bounds_size().x != 0.0
|
||||
&& World::get_bounds_size().y != 0.0)
|
||||
{
|
||||
let bounds = World::get_bounds_size();
|
||||
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")]
|
||||
{
|
||||
var_env_insert!(env, WAYLAND_DISPLAY);
|
||||
env.insert("GDK_BACKEND".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.insert("XDG_SESSION_TYPE".to_string(), "wayland".to_string());
|
||||
}
|
||||
env
|
||||
}
|
||||
|
||||
@@ -8,18 +8,20 @@ use crate::{
|
||||
wayland::surface::CoreSurface,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use portable_atomic::{AtomicU32, Ordering};
|
||||
use rand::Rng;
|
||||
use smithay::{
|
||||
backend::renderer::utils::{on_commit_buffer_handler, RendererSurfaceStateUserData},
|
||||
backend::renderer::utils::{RendererSurfaceStateUserData, on_commit_buffer_handler},
|
||||
delegate_compositor,
|
||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
||||
desktop::PopupKind,
|
||||
reexports::wayland_server::{Client, protocol::wl_surface::WlSurface},
|
||||
wayland::compositor::{
|
||||
self, add_post_commit_hook, CompositorClientState, CompositorHandler, CompositorState,
|
||||
CompositorClientState, CompositorHandler, CompositorState, add_post_commit_hook,
|
||||
},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
pub struct ConfiguredSurface;
|
||||
|
||||
impl CompositorHandler for WaylandState {
|
||||
fn compositor_state(&mut self) -> &mut CompositorState {
|
||||
@@ -30,19 +32,27 @@ impl CompositorHandler for WaylandState {
|
||||
debug!(?surface, "Surface commit");
|
||||
|
||||
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 {
|
||||
@@ -54,6 +64,7 @@ impl CompositorHandler for WaylandState {
|
||||
surface.insert_data(SurfaceId::Child(id));
|
||||
CoreSurface::add_to(surface);
|
||||
let Some(parent_surface_id) = parent.get_data::<SurfaceId>() else {
|
||||
warn!("Parent surface has no SurfaceId");
|
||||
return;
|
||||
};
|
||||
surface.insert_data(Mutex::new(ChildInfo {
|
||||
@@ -68,6 +79,7 @@ impl CompositorHandler for WaylandState {
|
||||
}));
|
||||
|
||||
let Some(panel_item) = surface_panel_item(parent) else {
|
||||
warn!("Parent has no panel item");
|
||||
return;
|
||||
};
|
||||
let panel_item_weak = Arc::downgrade(&panel_item);
|
||||
@@ -75,11 +87,19 @@ impl CompositorHandler for WaylandState {
|
||||
if surface_panel_item(surf).is_some() {
|
||||
return;
|
||||
}
|
||||
debug!("Linking surface to panel item");
|
||||
surf.insert_data(panel_item_weak.clone());
|
||||
|
||||
let Some(panel_item) = surface_panel_item(surf) else {
|
||||
warn!("Failed to link surface to panel item");
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -88,6 +108,7 @@ impl CompositorHandler for WaylandState {
|
||||
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|s| s.lock().ok()?.view())
|
||||
.flatten()
|
||||
else {
|
||||
debug!("No view data for surface");
|
||||
return;
|
||||
};
|
||||
let mut changed = false;
|
||||
@@ -96,11 +117,13 @@ impl CompositorHandler for WaylandState {
|
||||
&& info.geometry.origin.y != view.offset.y
|
||||
{
|
||||
changed = true;
|
||||
debug!("Surface position changed");
|
||||
}
|
||||
if info.geometry.size.x != view.dst.w as u32
|
||||
&& info.geometry.size.y != view.dst.h as u32
|
||||
{
|
||||
changed = true;
|
||||
debug!("Surface size changed");
|
||||
}
|
||||
info.geometry.size = [view.dst.w as u32, view.dst.h as u32].into();
|
||||
});
|
||||
@@ -109,6 +132,7 @@ impl CompositorHandler for WaylandState {
|
||||
return;
|
||||
};
|
||||
if changed {
|
||||
debug!("Repositioning child due to geometry change");
|
||||
panel_item.backend.reposition_child(surf);
|
||||
}
|
||||
});
|
||||
@@ -119,6 +143,7 @@ impl CompositorHandler for WaylandState {
|
||||
return;
|
||||
};
|
||||
if surface.get_child_info().is_some() {
|
||||
debug!("Dropping destroyed child surface");
|
||||
panel_item.backend.drop_child(surface);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use smithay::reexports::wayland_server::{
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
protocol::{
|
||||
wl_data_device::{
|
||||
Request::{Release, SetSelection, StartDrag},
|
||||
@@ -13,7 +14,6 @@ use smithay::reexports::wayland_server::{
|
||||
WlDataSource,
|
||||
},
|
||||
},
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
};
|
||||
|
||||
use super::state::WaylandState;
|
||||
|
||||
@@ -13,8 +13,8 @@ use smithay::{
|
||||
Mode as KdeMode, OrgKdeKwinServerDecoration,
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle,
|
||||
GlobalDispatch, New, Resource, WEnum, Weak,
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum, Weak,
|
||||
protocol::wl_surface::WlSurface,
|
||||
},
|
||||
},
|
||||
wayland::shell::{self, kde::decoration::KdeDecorationHandler},
|
||||
|
||||
@@ -23,8 +23,8 @@ mod generated {
|
||||
use super::state::WaylandState;
|
||||
use smithay::{
|
||||
backend::allocator::{
|
||||
dmabuf::{Dmabuf, DmabufFlags},
|
||||
Fourcc, Modifier,
|
||||
dmabuf::{Dmabuf, DmabufFlags},
|
||||
},
|
||||
reexports::wayland_server::{
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
|
||||
@@ -11,35 +11,35 @@ mod xdg_shell;
|
||||
|
||||
use self::{state::WaylandState, surface::CORE_SURFACES};
|
||||
use crate::{core::task, wayland::state::ClientState};
|
||||
use color_eyre::eyre::{ensure, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use color_eyre::eyre::{Result, ensure};
|
||||
use parking_lot::Mutex;
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::egl::EGLContext;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::{ImportDma, Renderer};
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_server::backend::ClientId;
|
||||
use smithay::reexports::wayland_server::DisplayHandle;
|
||||
use smithay::reexports::wayland_server::{Display, ListeningSocket};
|
||||
use smithay::wayland::dmabuf;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::dmabuf::Dmabuf,
|
||||
egl::EGLContext,
|
||||
renderer::{ImportDma, Renderer, gles::GlesRenderer},
|
||||
},
|
||||
output::Output,
|
||||
reexports::wayland_server::{Display, DisplayHandle, ListeningSocket},
|
||||
wayland::dmabuf,
|
||||
};
|
||||
use std::{
|
||||
ffi::c_void,
|
||||
os::unix::{net::UnixListener, prelude::FromRawFd},
|
||||
sync::Arc,
|
||||
ffi::{OsStr, c_void},
|
||||
os::fd::AsFd,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use stereokit_rust::system::{Backend, BackendGraphics};
|
||||
use tokio::io::unix::AsyncFdReadyGuard;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
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};
|
||||
|
||||
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
||||
pub static WAYLAND_DISPLAY: OnceLock<String> = OnceLock::new();
|
||||
|
||||
struct EGLRawHandles {
|
||||
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 {
|
||||
display: Arc<DisplayWrapper>,
|
||||
pub socket_name: Option<String>,
|
||||
join_handle: JoinHandle<Result<()>>,
|
||||
flush_notify: Arc<Notify>,
|
||||
client_listener: AbortHandle,
|
||||
client_dispatcher: AbortHandle,
|
||||
renderer: GlesRenderer,
|
||||
output: Output,
|
||||
dmabuf_rx: UnboundedReceiver<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||
@@ -114,7 +84,6 @@ impl Wayland {
|
||||
let display_handle = display.handle();
|
||||
|
||||
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 output = wayland_state.lock().output.clone();
|
||||
@@ -129,58 +98,78 @@ impl Wayland {
|
||||
}
|
||||
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 {
|
||||
display,
|
||||
socket_name,
|
||||
join_handle,
|
||||
flush_notify,
|
||||
client_listener,
|
||||
client_dispatcher,
|
||||
renderer,
|
||||
output,
|
||||
dmabuf_rx,
|
||||
})
|
||||
}
|
||||
|
||||
fn start_loop(
|
||||
display: Arc<DisplayWrapper>,
|
||||
async fn client_listener_loop(
|
||||
mut display_handle: DisplayHandle,
|
||||
socket: ListeningSocket,
|
||||
state: Arc<Mutex<WaylandState>>,
|
||||
) -> Result<JoinHandle<Result<()>>> {
|
||||
let listen_async =
|
||||
AsyncUnixListener::from_std(unsafe { UnixListener::from_raw_fd(socket.as_raw_fd()) })?;
|
||||
) -> Result<()> {
|
||||
let async_fd = AsyncFd::new(socket.as_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 dispatch_poll_listener = UnownedFd(Some(AsyncFd::new(dispatch_poll_fd)?));
|
||||
let stream = tokio::net::UnixStream::from_std(stream)?;
|
||||
let pid = stream.peer_cred().ok().and_then(|c| c.pid());
|
||||
|
||||
let dh1 = display.handle();
|
||||
let mut dh2 = dh1.clone();
|
||||
// New client connected
|
||||
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 {
|
||||
let _socket = socket; // Keep the socket alive
|
||||
loop {
|
||||
tokio::select! {
|
||||
acc = listen_async.accept() => { // New client connected
|
||||
let (stream, _) = acc?;
|
||||
let client_state = Arc::new(ClientState {
|
||||
pid: stream.peer_cred().ok().and_then(|c| c.pid()),
|
||||
id: OnceCell::new(),
|
||||
compositor_state: Default::default(),
|
||||
seat: state.lock().seat.clone(),
|
||||
});
|
||||
let _client = dh2.insert_client(stream.into_std()?, client_state.clone())?;
|
||||
}
|
||||
e = dispatch_poll_listener.readable() => { // Dispatch
|
||||
let mut guard = e?;
|
||||
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();
|
||||
}
|
||||
async fn dispatch_client_loop(
|
||||
mut display: Display<WaylandState>,
|
||||
flush_notify: Arc<Notify>,
|
||||
state: Arc<Mutex<WaylandState>>,
|
||||
) -> std::io::Result<()> {
|
||||
loop {
|
||||
let poll_fd = display.backend().poll_fd();
|
||||
let async_fd = AsyncFd::new(poll_fd)?;
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = async_fd.readable() => {
|
||||
drop(async_fd);
|
||||
let _span = debug_span!("Dispatch wayland event");
|
||||
let _span = _span.enter();
|
||||
let _ = display.dispatch_clients(&mut *state.lock());
|
||||
let _ = display.flush_clients();
|
||||
}
|
||||
_ = flush_notify.notified() => {
|
||||
drop(async_fd);
|
||||
let _ = display.flush_clients();
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "Wayland frame", skip(self))]
|
||||
@@ -197,7 +186,7 @@ impl Wayland {
|
||||
}
|
||||
let _ = self.renderer.cleanup_texture_cache();
|
||||
|
||||
self.display.flush_clients(None);
|
||||
self.flush_notify.notify_waiters();
|
||||
}
|
||||
|
||||
pub fn frame_event(&self) {
|
||||
@@ -214,6 +203,7 @@ impl Wayland {
|
||||
}
|
||||
impl Drop for Wayland {
|
||||
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 crate::{
|
||||
core::task,
|
||||
nodes::{
|
||||
data::KEYMAPS,
|
||||
items::panel::{Backend, Geometry, PanelItem},
|
||||
},
|
||||
nodes::items::panel::{Backend, Geometry, KEYMAPS, PanelItem},
|
||||
};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
@@ -14,12 +11,12 @@ use smithay::{
|
||||
backend::input::{AxisRelativeDirection, ButtonState, KeyState},
|
||||
delegate_seat,
|
||||
input::{
|
||||
keyboard::{FilterResult, LedState},
|
||||
pointer::{AxisFrame, ButtonEvent, CursorImageStatus, MotionEvent},
|
||||
touch::{self, DownEvent, UpEvent},
|
||||
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,
|
||||
wayland::compositor,
|
||||
};
|
||||
@@ -41,6 +38,11 @@ impl SeatHandler for WaylandState {
|
||||
CursorImageStatus::Surface(surface) => {
|
||||
CoreSurface::add_to(&surface);
|
||||
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>>() {
|
||||
core_surface.set_material_offset(1);
|
||||
}
|
||||
@@ -106,6 +108,12 @@ impl SeatWrapper {
|
||||
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) {
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
if pointer.current_focus() == Some(surface.clone()) {
|
||||
@@ -203,7 +211,7 @@ impl SeatWrapper {
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
@@ -226,20 +234,18 @@ impl SeatWrapper {
|
||||
{
|
||||
return;
|
||||
}
|
||||
for key in keys {
|
||||
keyboard.input(
|
||||
&mut state.lock(),
|
||||
key.unsigned_abs(),
|
||||
if key > 0 {
|
||||
KeyState::Pressed
|
||||
} else {
|
||||
KeyState::Released
|
||||
},
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
0,
|
||||
|_, _, _| FilterResult::Forward::<()>,
|
||||
);
|
||||
}
|
||||
keyboard.input(
|
||||
&mut state.lock(),
|
||||
key.into(),
|
||||
if pressed {
|
||||
KeyState::Pressed
|
||||
} else {
|
||||
KeyState::Released
|
||||
},
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
0,
|
||||
|_, _, _| FilterResult::Forward::<()>,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn touch_down(&self, surface: WlSurface, id: u32, position: Vector2<f32>) {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use super::seat::SeatWrapper;
|
||||
use crate::wayland::drm::wl_drm::WlDrm;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::{dmabuf::Dmabuf, Fourcc},
|
||||
allocator::{Fourcc, dmabuf::Dmabuf},
|
||||
egl::EGLDevice,
|
||||
renderer::gles::GlesRenderer,
|
||||
},
|
||||
delegate_dmabuf, delegate_output, delegate_shm,
|
||||
input::{keyboard::XkbConfig, SeatState},
|
||||
delegate_dmabuf, delegate_output, delegate_shm, delegate_viewporter,
|
||||
desktop::PopupManager,
|
||||
input::{SeatState, keyboard::XkbConfig},
|
||||
output::{Mode, Output, Scale, Subpixel},
|
||||
reexports::{
|
||||
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_server::{
|
||||
DisplayHandle,
|
||||
backend::{ClientData, ClientId, DisconnectReason},
|
||||
protocol::{
|
||||
wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager,
|
||||
wl_output::WlOutput,
|
||||
},
|
||||
DisplayHandle,
|
||||
},
|
||||
},
|
||||
utils::{Size, Transform},
|
||||
@@ -39,15 +39,16 @@ use smithay::{
|
||||
xdg::{WmCapabilitySet, XdgShellState},
|
||||
},
|
||||
shm::{ShmHandler, ShmState},
|
||||
viewporter::ViewporterState,
|
||||
},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub struct ClientState {
|
||||
pub pid: Option<i32>,
|
||||
pub id: OnceCell<ClientId>,
|
||||
pub id: OnceLock<ClientId>,
|
||||
pub compositor_state: CompositorClientState,
|
||||
pub seat: Arc<SeatWrapper>,
|
||||
}
|
||||
@@ -71,11 +72,13 @@ pub struct WaylandState {
|
||||
pub kde_decoration_state: KdeDecorationState,
|
||||
pub shm_state: ShmState,
|
||||
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
||||
pub _viewporter_state: ViewporterState,
|
||||
pub drm_formats: Vec<Fourcc>,
|
||||
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||
pub seat_state: SeatState<Self>,
|
||||
pub seat: Arc<SeatWrapper>,
|
||||
pub xdg_shell: XdgShellState,
|
||||
pub popup_manager: PopupManager,
|
||||
pub output: Output,
|
||||
}
|
||||
|
||||
@@ -158,6 +161,8 @@ impl WaylandState {
|
||||
output.set_preferred(mode);
|
||||
|
||||
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();
|
||||
capabilities.set(WmCapabilities::Maximize);
|
||||
capabilities.set(WmCapabilities::Fullscreen);
|
||||
@@ -182,7 +187,9 @@ impl WaylandState {
|
||||
seat_state,
|
||||
seat: Arc::new(SeatWrapper::new(weak.clone(), seat)),
|
||||
xdg_shell,
|
||||
popup_manager,
|
||||
output,
|
||||
_viewporter_state,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -220,3 +227,4 @@ impl OutputHandler for WaylandState {
|
||||
delegate_dmabuf!(WaylandState);
|
||||
delegate_shm!(WaylandState);
|
||||
delegate_output!(WaylandState);
|
||||
delegate_viewporter!(WaylandState);
|
||||
|
||||
@@ -9,20 +9,23 @@ use crate::{
|
||||
items::camera::TexWrapper,
|
||||
},
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use send_wrapper::SendWrapper;
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
gles::{GlesRenderer, GlesTexture},
|
||||
utils::{import_surface_tree, RendererSurfaceStateUserData},
|
||||
Renderer, Texture,
|
||||
gles::{GlesRenderer, GlesTexture},
|
||||
utils::{RendererSurfaceStateUserData, import_surface_tree},
|
||||
},
|
||||
desktop::utils::send_frames_surface_tree,
|
||||
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::{
|
||||
material::{Material, Transparency},
|
||||
shader::Shader,
|
||||
@@ -44,8 +47,8 @@ impl Drop for CoreSurfaceData {
|
||||
pub struct CoreSurface {
|
||||
pub weak_surface: wayland_server::Weak<WlSurface>,
|
||||
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
||||
sk_tex: OnceCell<Mutex<TexWrapper>>,
|
||||
sk_mat: OnceCell<Mutex<MaterialWrapper>>,
|
||||
sk_tex: OnceLock<Mutex<TexWrapper>>,
|
||||
sk_mat: OnceLock<Mutex<MaterialWrapper>>,
|
||||
material_offset: Mutex<Delta<u32>>,
|
||||
pub pending_material_applications: Registry<ModelPart>,
|
||||
}
|
||||
@@ -55,8 +58,8 @@ impl CoreSurface {
|
||||
let core_surface = CORE_SURFACES.add(CoreSurface {
|
||||
weak_surface: surface.downgrade(),
|
||||
mapped_data: Mutex::new(None),
|
||||
sk_tex: OnceCell::new(),
|
||||
sk_mat: OnceCell::new(),
|
||||
sk_tex: OnceLock::new(),
|
||||
sk_mat: OnceLock::new(),
|
||||
material_offset: Mutex::new(Delta::new(0)),
|
||||
pending_material_applications: Registry::new(),
|
||||
});
|
||||
@@ -92,7 +95,8 @@ impl CoreSurface {
|
||||
});
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -104,25 +108,11 @@ impl CoreSurface {
|
||||
let Some(wl_surface) = self.wl_surface() else {
|
||||
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
|
||||
.get_data_raw::<RendererSurfaceStateUserData, _, _>(|surface_states| {
|
||||
surface_states
|
||||
.lock()
|
||||
.unwrap()
|
||||
.texture::<GlesRenderer>(renderer.id())
|
||||
.cloned()
|
||||
let locked = surface_states.lock().unwrap();
|
||||
locked.texture::<GlesRenderer>(renderer.id()).cloned()
|
||||
})
|
||||
.flatten()
|
||||
else {
|
||||
@@ -130,9 +120,11 @@ impl CoreSurface {
|
||||
};
|
||||
|
||||
let Some(sk_tex) = self.sk_tex.get() else {
|
||||
tracing::error!("No sk_tex found for surface");
|
||||
return;
|
||||
};
|
||||
let Some(sk_mat) = self.sk_mat.get() else {
|
||||
tracing::error!("No sk_mat found for surface");
|
||||
return;
|
||||
};
|
||||
sk_tex
|
||||
@@ -157,7 +149,7 @@ impl CoreSurface {
|
||||
let new_mapped_data = CoreSurfaceData {
|
||||
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) {
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
use super::{
|
||||
seat::{handle_cursor, SeatWrapper},
|
||||
seat::{SeatWrapper, handle_cursor},
|
||||
state::{ClientState, WaylandState},
|
||||
surface::CoreSurface,
|
||||
utils::*,
|
||||
};
|
||||
use crate::nodes::{
|
||||
drawable::model::ModelPart,
|
||||
items::panel::{
|
||||
Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceId, ToplevelInfo,
|
||||
use crate::{
|
||||
core::error::Result,
|
||||
nodes::{
|
||||
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 parking_lot::Mutex;
|
||||
use rand::Rng;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smithay::{
|
||||
delegate_xdg_shell,
|
||||
desktop::PopupKind,
|
||||
reexports::{
|
||||
wayland_protocols::xdg::{
|
||||
decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode,
|
||||
shell::server::xdg_toplevel::{ResizeEdge, State},
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||
Resource,
|
||||
protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||
},
|
||||
},
|
||||
utils::{Logical, Rectangle, Serial},
|
||||
@@ -87,7 +91,6 @@ impl XdgShellHandler for WaylandState {
|
||||
s.states.set(State::Maximized);
|
||||
s.states.unset(State::Fullscreen);
|
||||
});
|
||||
toplevel.send_configure();
|
||||
|
||||
let initial_size = toplevel
|
||||
.wl_surface()
|
||||
@@ -222,14 +225,17 @@ impl XdgShellHandler for WaylandState {
|
||||
};
|
||||
panel_item.toplevel_title_changed(&title)
|
||||
}
|
||||
|
||||
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);
|
||||
popup.wl_surface().insert_data(SurfaceId::Child(id));
|
||||
let Some(parent) = popup.get_parent_surface() else {
|
||||
warn!("No parent surface found for popup");
|
||||
return;
|
||||
};
|
||||
let _ = popup.send_configure();
|
||||
CoreSurface::add_to(popup.wl_surface());
|
||||
|
||||
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 {
|
||||
warn!("No panel item found for popup parent");
|
||||
return;
|
||||
};
|
||||
let panel_item_weak = Arc::downgrade(&panel_item);
|
||||
@@ -252,6 +259,7 @@ impl XdgShellHandler for WaylandState {
|
||||
}
|
||||
surf.insert_data(panel_item_weak.clone());
|
||||
let Some(panel) = surface_panel_item(surf) else {
|
||||
warn!("Failed to get panel item for popup surface");
|
||||
return;
|
||||
};
|
||||
panel.backend.new_child(surf);
|
||||
@@ -464,6 +472,7 @@ impl Backend for XdgBackend {
|
||||
|
||||
fn close_toplevel(&self) {
|
||||
if let Some(toplevel) = self.toplevel.lock().clone() {
|
||||
self.seat.unfocus_internal_state(toplevel.wl_surface());
|
||||
toplevel.send_close();
|
||||
}
|
||||
}
|
||||
@@ -515,11 +524,11 @@ impl Backend for XdgBackend {
|
||||
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 {
|
||||
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>) {
|
||||
|
||||
Reference in New Issue
Block a user