Compare commits
313 Commits
camera-ite
...
0.44.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
643986e03d | ||
|
|
a2b061ed43 | ||
|
|
c458624157 | ||
|
|
1d2b149395 | ||
|
|
c84848ea9c | ||
|
|
b34e5f7a19 | ||
|
|
af3d49c9ef | ||
|
|
d5d63b2f89 | ||
|
|
d4b7c3f61a | ||
|
|
36dacb3322 | ||
|
|
eec38dd60f | ||
|
|
1e0ea6ae92 | ||
|
|
31f45760f0 | ||
|
|
f76863a79b | ||
|
|
43b3499ed7 | ||
|
|
dfaaa2a3a9 | ||
|
|
b8d17ac7ca | ||
|
|
ed28914f86 | ||
|
|
f8fab6bf5a | ||
|
|
1b37d77304 | ||
|
|
6eb36516b0 | ||
|
|
f0200be990 | ||
|
|
cfd3d0016b | ||
|
|
fbf0f4f672 | ||
|
|
387fa96a60 | ||
|
|
d549018024 | ||
|
|
5cd92f2c03 | ||
|
|
2450411e21 | ||
|
|
b53bfde23b | ||
|
|
15eb73af41 | ||
|
|
3c82ae309c | ||
|
|
4adcfca2fe | ||
|
|
f893491bed | ||
|
|
9d1181aaca | ||
|
|
b0f9bf24cf | ||
|
|
3527ce2507 | ||
|
|
f045bfb93d | ||
|
|
3d4ab27a14 | ||
|
|
54ff87c146 | ||
|
|
2bc988fe3d | ||
|
|
051893858b | ||
|
|
1ac211c23f | ||
|
|
4874f010dd | ||
|
|
da894143f9 | ||
|
|
109affec81 | ||
|
|
665e6b034f | ||
|
|
fc45b4e400 | ||
|
|
af75d2a451 | ||
|
|
3136a8f2b7 | ||
|
|
e97368f3e2 | ||
|
|
4da7ed1ccf | ||
|
|
bf248e192f | ||
|
|
167c3d1cbf | ||
|
|
b39d8f37f4 | ||
|
|
1198797db8 | ||
|
|
823a71a286 | ||
|
|
f78da4b198 | ||
|
|
558fb1aa4e | ||
|
|
abe32a8dbd | ||
|
|
d9e040bf8b | ||
|
|
0b8da1d280 | ||
|
|
455f3a0e9c | ||
|
|
411b30294b | ||
|
|
1d54b75a53 | ||
|
|
4c56c31bfc | ||
|
|
b21e031668 | ||
|
|
7c6b2f5949 | ||
|
|
1f66d7dee4 | ||
|
|
53b035ef92 | ||
|
|
237b084a65 | ||
|
|
707e56cb6f | ||
|
|
6f4da69e36 | ||
|
|
5634729445 | ||
|
|
b9603dc0a1 | ||
|
|
0323fbfb86 | ||
|
|
433568da63 | ||
|
|
4e199dd43f | ||
|
|
a4430b9a95 | ||
|
|
136383326e | ||
|
|
5a86f11beb | ||
|
|
6ab2bb2d52 | ||
|
|
ce8877b67e | ||
|
|
74a2f7a249 | ||
|
|
11ecb0aebe | ||
|
|
281f5e91ff | ||
|
|
5dc7cfbe83 | ||
|
|
3432c63a6e | ||
|
|
02ac96b0dc | ||
|
|
0736f99631 | ||
|
|
4bbe3ad8d0 | ||
|
|
1cf9d0f8c5 | ||
|
|
51d0cab832 | ||
|
|
062c63af2b | ||
|
|
3ce3fadb8d | ||
|
|
f0e39195b7 | ||
|
|
0d639760e9 | ||
|
|
6109a6bde6 | ||
|
|
000b633767 | ||
|
|
e3b1276d77 | ||
|
|
1cb8e1b7a4 | ||
|
|
e879b724ec | ||
|
|
08010efa46 | ||
|
|
e682931e3e | ||
|
|
bf89b73e8f | ||
|
|
2e252279bb | ||
|
|
9cf43ec535 | ||
|
|
f15578f7df | ||
|
|
f63ca4a25b | ||
|
|
89741508e3 | ||
|
|
81be807749 | ||
|
|
fcdb8a7edf | ||
|
|
90ce185f29 | ||
|
|
d6353035ae | ||
|
|
ceb1b23264 | ||
|
|
199e6f70b3 | ||
|
|
641db4face | ||
|
|
80d292b511 | ||
|
|
7fbcc92d02 | ||
|
|
de46726d01 | ||
|
|
6efa3a909e | ||
|
|
ea0f174da7 | ||
|
|
444146fa21 | ||
|
|
a7930760e8 | ||
|
|
668c32f583 | ||
|
|
927e1c48e2 | ||
|
|
8cc20e054c | ||
|
|
b12b171b53 | ||
|
|
0e61d51072 | ||
|
|
e61c04960e | ||
|
|
5dc82be1a3 | ||
|
|
6861b92972 | ||
|
|
f68f350cd2 | ||
|
|
2820415373 | ||
|
|
f721a57604 | ||
|
|
fb4149eaa7 | ||
|
|
d3746ef787 | ||
|
|
9d4b4bee4d | ||
|
|
5390b0effb | ||
|
|
13da4c8d60 | ||
|
|
1740d55f9c | ||
|
|
52d5e97de6 | ||
|
|
633df045d4 | ||
|
|
415bf5bb04 | ||
|
|
4e2d4a15c9 | ||
|
|
ef0142183d | ||
|
|
e5dfd9d3df | ||
|
|
6773fe2cf3 | ||
|
|
5a6e7e02ca | ||
|
|
c5d8ec2ef1 | ||
|
|
a31781146e | ||
|
|
cb9368cb8e | ||
|
|
629c05e507 | ||
|
|
9123153bf3 | ||
|
|
f3dc632ffc | ||
|
|
c369100d8a | ||
|
|
e10d40ef5e | ||
|
|
d6ca367187 | ||
|
|
88ac8a8b86 | ||
|
|
70fef89e2d | ||
|
|
4d79a59b20 | ||
|
|
c776c1b712 | ||
|
|
d4de15e0b3 | ||
|
|
9d220ec235 | ||
|
|
09c6c010e2 | ||
|
|
c9e185e9f3 | ||
|
|
4737149c85 | ||
|
|
648451b47e | ||
|
|
a9ef2d6f4b | ||
|
|
d6ffcadd76 | ||
|
|
448b7489e8 | ||
|
|
622cf60a65 | ||
|
|
1ab11f1660 | ||
|
|
9654e6cc59 | ||
|
|
44d177858f | ||
|
|
be41f11b83 | ||
|
|
dd2bffc2b1 | ||
|
|
d2ef508607 | ||
|
|
0cc7c7bc24 | ||
|
|
8d65e304cb | ||
|
|
b0dbccbd18 | ||
|
|
a823fbfb57 | ||
|
|
4a864e6519 | ||
|
|
e23d847449 | ||
|
|
8ba199f053 | ||
|
|
23925b4475 | ||
|
|
7ea0220f33 | ||
|
|
969e4de882 | ||
|
|
e5acb3013f | ||
|
|
3d57bed1c0 | ||
|
|
45839ebf60 | ||
|
|
0bb5b53e02 | ||
|
|
4f966b6d71 | ||
|
|
2687a393b5 | ||
|
|
5c605932ef | ||
|
|
bccdc8221e | ||
|
|
bddf17bbef | ||
|
|
e8511e8759 | ||
|
|
85296f538b | ||
|
|
f8ff80b781 | ||
|
|
932fef87f5 | ||
|
|
742780e34e | ||
|
|
41ede661f7 | ||
|
|
8d85460803 | ||
|
|
ac71581db8 | ||
|
|
2f894c4058 | ||
|
|
2b97c98a6e | ||
|
|
98d9f491ba | ||
|
|
16d710e106 | ||
|
|
9ad202e778 | ||
|
|
b3747d623c | ||
|
|
d5ff9281e6 | ||
|
|
18ebd8c522 | ||
|
|
411f71c217 | ||
|
|
3027ae20a9 | ||
|
|
fbce321426 | ||
|
|
74bc3a306e | ||
|
|
a950ad59f1 | ||
|
|
cf840da444 | ||
|
|
173fba35fa | ||
|
|
97fbbec0fe | ||
|
|
400f3a23bf | ||
|
|
1ad3336b6f | ||
|
|
8e9956abe1 | ||
|
|
6ca93ea24c | ||
|
|
49810e8fd1 | ||
|
|
afd0946558 | ||
|
|
fd31d0cd99 | ||
|
|
2f380da62f | ||
|
|
1c6971cd11 | ||
|
|
da4cf084d2 | ||
|
|
ca95ed5461 | ||
|
|
1b06cb6952 | ||
|
|
df89c826bb | ||
|
|
21f7f66440 | ||
|
|
3f1bad18c8 | ||
|
|
0c190cc833 | ||
|
|
5f0df8e7c1 | ||
|
|
d715f2f9ed | ||
|
|
d7fa4e62b8 | ||
|
|
568ebb0060 | ||
|
|
42efc67625 | ||
|
|
dd4b0097a1 | ||
|
|
a483cdbc7d | ||
|
|
84a7546442 | ||
|
|
dd43f238ff | ||
|
|
4f057358c8 | ||
|
|
e20971aef7 | ||
|
|
eb0d3c5bcf | ||
|
|
a18222e3df | ||
|
|
93ca932da9 | ||
|
|
c512b2fef5 | ||
|
|
f53c684377 | ||
|
|
3552166207 | ||
|
|
0b6eb147c5 | ||
|
|
1833ed50f3 | ||
|
|
6cdbfb3bad | ||
|
|
a5e0cb19c9 | ||
|
|
519ab94312 | ||
|
|
f2a8c0ed13 | ||
|
|
b3998f315d | ||
|
|
40bcd61b98 | ||
|
|
7a4d557c61 | ||
|
|
303b3f3ca2 | ||
|
|
ac5e949614 | ||
|
|
60baabb850 | ||
|
|
248e48fd8e | ||
|
|
b9baee7e5f | ||
|
|
3598ffdbb1 | ||
|
|
c171d9e6db | ||
|
|
d7a607a663 | ||
|
|
03ccf9127d | ||
|
|
6a3024657f | ||
|
|
a0058fcc2e | ||
|
|
410cc13c4f | ||
|
|
bc259dbe01 | ||
|
|
3730e20248 | ||
|
|
1be413065d | ||
|
|
2721c20c8b | ||
|
|
80130f6ffd | ||
|
|
8da778eaba | ||
|
|
3c708d1aaf | ||
|
|
1ae1bef3c1 | ||
|
|
7fd0c1fddb | ||
|
|
fd9957b784 | ||
|
|
57da02dbad | ||
|
|
83a5b36ddc | ||
|
|
8c36d73775 | ||
|
|
8396b98f67 | ||
|
|
46d989ce7f | ||
|
|
75ac570486 | ||
|
|
242def9d06 | ||
|
|
959f32009b | ||
|
|
57796c217d | ||
|
|
cea3390e36 | ||
|
|
a756e80064 | ||
|
|
1f61d32877 | ||
|
|
da7e2c5e6e | ||
|
|
fd0940bfe9 | ||
|
|
2b4a495c07 | ||
|
|
cffb968d2e | ||
|
|
201ab3aee8 | ||
|
|
cfa3584dda | ||
|
|
f19ba93958 | ||
|
|
09a2572c3b | ||
|
|
c6316b4e8b | ||
|
|
9cd900b23f | ||
|
|
a6d30cb366 | ||
|
|
3e94a3f62a | ||
|
|
060f8264ff | ||
|
|
b7b3907647 | ||
|
|
1550555df1 | ||
|
|
c42a29a034 | ||
|
|
621bf6b82a |
371
Cargo.lock
generated
371
Cargo.lock
generated
@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.2"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -55,9 +55,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.4"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
@@ -69,15 +69,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.4"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.2"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
@@ -93,9 +93,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.1"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@@ -133,13 +133,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.74"
|
version = "0.1.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
|
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -261,9 +261,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.1"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
@@ -282,14 +282,14 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
@@ -299,11 +299,11 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "calloop"
|
name = "calloop"
|
||||||
version = "0.12.3"
|
version = "0.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf"
|
checksum = "aadd183e815348c0649051b1c43418643208f8ed13c8a84da7215b4e1cf42714"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"log",
|
"log",
|
||||||
"polling",
|
"polling",
|
||||||
"rustix",
|
"rustix",
|
||||||
@@ -358,9 +358,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.6"
|
version = "4.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -368,9 +368,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.4.6"
|
version = "4.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -387,7 +387,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -454,7 +454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d"
|
checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -502,6 +502,15 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -542,9 +551,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cursor-icon"
|
name = "cursor-icon"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "740bb192a8e2d1350119916954f4409ee7f62f149b536911eeb78ba5a20526bf"
|
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
@@ -582,7 +591,7 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libloading 0.8.1",
|
"libloading 0.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -593,25 +602,25 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drm"
|
name = "drm"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97fb1b703ffbc7ebd216eba7900008049a56ace55580ecb2ee7fa801e8d8be87"
|
checksum = "e58eefd79f5173683872c0c82d0f05c2dc3c583d631259f60bb7a323756b7ff2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"drm-ffi",
|
"drm-ffi",
|
||||||
"drm-fourcc",
|
"drm-fourcc",
|
||||||
"nix 0.27.1",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drm-ffi"
|
name = "drm-ffi"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba7d1c19c4b6270e89d59fb27dc6d02a317c658a8a54e54781e1db9b5947595d"
|
checksum = "220dd8c12ebf2b0cbaffa19e00de02f5f090d363fb900f16ea012c077eea1174"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"drm-sys",
|
"drm-sys",
|
||||||
"nix 0.27.1",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -619,15 +628,16 @@ name = "drm-fourcc"
|
|||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drm-sys"
|
name = "drm-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a4f1c0468062a56cd5705f1e3b5409eb286d5596a2028ec8e947595d7e715ae"
|
checksum = "5115283ec60c99da8a9e5dc3c55f27680211e974c948cb6f3b51f0373190503b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys 0.6.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
@@ -652,9 +662,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.5"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@@ -688,9 +698,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.28"
|
version = "1.0.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
@@ -776,6 +786,24 @@ dependencies = [
|
|||||||
"pin-utils",
|
"pin-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxhash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxtypemap"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c11c87c936dd5cbf3389179749cf020d886f32cc577fc214a7a65eaac5c569db"
|
||||||
|
dependencies = [
|
||||||
|
"fxhash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generator"
|
name = "generator"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
@@ -895,9 +923,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.1"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
|
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hdrhistogram"
|
name = "hdrhistogram"
|
||||||
@@ -1036,12 +1064,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.0.2"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
|
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.1",
|
"hashbrown 0.14.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1089,6 +1117,17 @@ version = "1.0.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kdl"
|
||||||
|
version = "4.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "062c875482ccb676fd40c804a40e3824d4464c18c364547456d1c8e8e951ae47"
|
||||||
|
dependencies = [
|
||||||
|
"miette",
|
||||||
|
"nom",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "khronos_api"
|
name = "khronos_api"
|
||||||
version = "3.1.0"
|
version = "3.1.0"
|
||||||
@@ -1109,9 +1148,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.149"
|
version = "0.2.150"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@@ -1125,9 +1164,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.1"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
|
checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@@ -1135,9 +1174,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.10"
|
version = "0.4.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da4a7ec558fa3b65e4c69b6af8df01fb9ad51ac69262335e1505276bc091935d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@@ -1177,7 +1222,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1197,15 +1242,15 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
version = "2.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
|
checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -1220,12 +1265,26 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "miette"
|
||||||
version = "0.9.0"
|
version = "5.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"miette-derive",
|
||||||
|
"once_cell",
|
||||||
|
"thiserror",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miette-derive"
|
||||||
|
version = "5.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1293,7 +1352,7 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"memoffset 0.7.1",
|
"memoffset",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1303,10 +1362,9 @@ version = "0.27.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"memoffset 0.9.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1331,9 +1389,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.17"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
@@ -1387,7 +1445,7 @@ dependencies = [
|
|||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1433,7 +1491,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1525,7 +1583,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1548,9 +1606,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polling"
|
name = "polling"
|
||||||
version = "3.2.0"
|
version = "3.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62a79e457c9898100b4298d57d69ec53d06f9a6ed352431ce5f377e082d2e846"
|
checksum = "7571075a670bb8e02350c4d1c27d934aabdce416aa91a95d58dc9e21267dad3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
@@ -1619,9 +1677,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.69"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -1642,7 +1700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "eb156a45b6b9fe8027497422179fb65afc84d36707a7ca98297bf06bccb8d43f"
|
checksum = "eb156a45b6b9fe8027497422179fb65afc84d36707a7ca98297bf06bccb8d43f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1756,14 +1814,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.2"
|
version = "1.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.3",
|
"regex-automata 0.3.8",
|
||||||
"regex-syntax 0.8.2",
|
"regex-syntax 0.7.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1777,13 +1835,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.3"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.8.2",
|
"regex-syntax 0.7.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1794,9 +1852,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.2"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
@@ -1821,14 +1879,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.19"
|
version = "0.38.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
|
checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys 0.4.11",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1864,9 +1922,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.20"
|
version = "1.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "send_wrapper"
|
name = "send_wrapper"
|
||||||
@@ -1876,22 +1934,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.189"
|
version = "1.0.188"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.189"
|
version = "1.0.188"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1913,14 +1971,14 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
@@ -1958,10 +2016,10 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/smithay/smithay.git#fb1afbf63e58d15b715251cf0f608ef1c0de6e6d"
|
source = "git+https://github.com/smithay/smithay.git#5c688b89fc97ade8c60c45d4a319311b7ec5292f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"calloop",
|
"calloop",
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"cursor-icon",
|
"cursor-icon",
|
||||||
@@ -1970,15 +2028,16 @@ dependencies = [
|
|||||||
"drm-ffi",
|
"drm-ffi",
|
||||||
"drm-fourcc",
|
"drm-fourcc",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
|
"errno",
|
||||||
"gl_generator",
|
"gl_generator",
|
||||||
"indexmap 2.0.2",
|
"indexmap 2.0.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"libloading 0.8.1",
|
"libloading 0.8.0",
|
||||||
"nix 0.27.1",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"profiling",
|
"profiling",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rustix",
|
||||||
"scan_fmt",
|
"scan_fmt",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -2013,15 +2072,20 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "split-iter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr"
|
name = "stardust-xr"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
source = "git+https://github.com/StardustXR/core.git?branch=camera-item#272f961be2c7e0369626ce70e56c266aefccef14"
|
source = "git+https://github.com/StardustXR/core.git#ddb2953f6acac01ee9c4ccfae2d4b7eb959a07a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cluFlock",
|
"cluFlock",
|
||||||
"color-rs",
|
"color-rs",
|
||||||
"dirs",
|
"dirs",
|
||||||
"drm-fourcc",
|
|
||||||
"global_counter",
|
"global_counter",
|
||||||
"mint",
|
"mint",
|
||||||
"nix 0.26.4",
|
"nix 0.26.4",
|
||||||
@@ -2037,11 +2101,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr-schemas"
|
name = "stardust-xr-schemas"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
source = "git+https://github.com/StardustXR/core.git?branch=camera-item#272f961be2c7e0369626ce70e56c266aefccef14"
|
source = "git+https://github.com/StardustXR/core.git#ddb2953f6acac01ee9c4ccfae2d4b7eb959a07a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flatbuffers",
|
"flatbuffers",
|
||||||
"flexbuffers",
|
"flexbuffers",
|
||||||
"glam 0.24.2",
|
"glam 0.24.2",
|
||||||
|
"kdl",
|
||||||
"manifest-dir-macros",
|
"manifest-dir-macros",
|
||||||
"mint",
|
"mint",
|
||||||
"ouroboros",
|
"ouroboros",
|
||||||
@@ -2052,15 +2117,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr-server"
|
name = "stardust-xr-server"
|
||||||
version = "0.43.0"
|
version = "0.44.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"clap",
|
"clap",
|
||||||
|
"cluFlock",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"directories",
|
"directories",
|
||||||
"drm-fourcc",
|
"fxtypemap",
|
||||||
"glam 0.23.0",
|
"glam 0.23.0",
|
||||||
"global_counter",
|
"global_counter",
|
||||||
"input-event-codes",
|
"input-event-codes",
|
||||||
@@ -2068,6 +2134,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"mint",
|
"mint",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
|
"nix 0.27.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
@@ -2079,14 +2146,29 @@ dependencies = [
|
|||||||
"serde_repr",
|
"serde_repr",
|
||||||
"smithay",
|
"smithay",
|
||||||
"stardust-xr",
|
"stardust-xr",
|
||||||
|
"stardust-xr-server-codegen",
|
||||||
"stereokit",
|
"stereokit",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-tracy",
|
"tracing-tracy",
|
||||||
|
"wayland-backend",
|
||||||
|
"wayland-scanner",
|
||||||
"xkbcommon",
|
"xkbcommon",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stardust-xr-server-codegen"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"mint",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"split-iter",
|
||||||
|
"stardust-xr-schemas",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -2140,9 +2222,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.38"
|
version = "2.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2170,22 +2252,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.49"
|
version = "1.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
|
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.49"
|
version = "1.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
|
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2200,9 +2282,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.33.0"
|
version = "1.32.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
|
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -2236,7 +2318,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2276,7 +2358,7 @@ version = "0.19.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.2",
|
"indexmap 2.0.0",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
@@ -2343,10 +2425,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.39"
|
version = "0.1.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
|
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-attributes",
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
@@ -2354,20 +2437,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.27"
|
version = "0.1.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.38",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.32"
|
version = "0.1.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"valuable",
|
"valuable",
|
||||||
@@ -2445,6 +2528,18 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -2498,7 +2593,7 @@ version = "0.31.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c"
|
checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
"wayland-scanner",
|
"wayland-scanner",
|
||||||
"wayland-server",
|
"wayland-server",
|
||||||
@@ -2510,7 +2605,7 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfa5933740b200188c9b4c38601b8212e8c154d7de0d2cb171944e137a77de1e"
|
checksum = "bfa5933740b200188c9b4c38601b8212e8c154d7de0d2cb171944e137a77de1e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"wayland-scanner",
|
"wayland-scanner",
|
||||||
@@ -2523,7 +2618,7 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
|
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"wayland-scanner",
|
"wayland-scanner",
|
||||||
@@ -2547,7 +2642,7 @@ version = "0.31.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f3f0c52a445936ca1184c98f1a69cf4ad9c9130788884531ef04428468cb1ce"
|
checksum = "3f3f0c52a445936ca1184c98f1a69cf4ad9c9130788884531ef04428468cb1ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.0",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
"nix 0.26.4",
|
"nix 0.26.4",
|
||||||
@@ -2686,9 +2781,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.17"
|
version = "0.5.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c"
|
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -2717,9 +2812,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xkbcommon"
|
name = "xkbcommon"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c286371c44b3572d19b09196c129a8fff47d7704d6494daefb44fec10f0278ab"
|
checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
|
|||||||
21
Cargo.toml
21
Cargo.toml
@@ -1,13 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
rust-version = "1.75"
|
||||||
name = "stardust-xr-server"
|
name = "stardust-xr-server"
|
||||||
version = "0.43.0"
|
version = "0.44.0"
|
||||||
authors = ["Nova King <technobaboo@proton.me>"]
|
authors = ["Nova King <technobaboo@proton.me>"]
|
||||||
description = "Stardust XR reference display server"
|
description = "Stardust XR reference display server"
|
||||||
license = "GPLv2"
|
license = "GPLv2"
|
||||||
repository = "https://github.com/StardustXR/stardust-xr-server/"
|
repository = "https://github.com/StardustXR/stardust-xr-server/"
|
||||||
homepage = "https://stardustxr.org"
|
homepage = "https://stardustxr.org"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["codegen"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "stardust-xr-server"
|
name = "stardust-xr-server"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
@@ -15,7 +19,8 @@ path = "src/main.rs"
|
|||||||
[features]
|
[features]
|
||||||
default = ["wayland"]
|
default = ["wayland"]
|
||||||
wayland = ["dep:smithay", "dep:xkbcommon"]
|
wayland = ["dep:smithay", "dep:xkbcommon"]
|
||||||
xwayland = ["smithay/xwayland"]
|
xwayland_rootful = []
|
||||||
|
xwayland_rootless = ["smithay/xwayland"]
|
||||||
profile_tokio = ["dep:console-subscriber", "tokio/tracing"]
|
profile_tokio = ["dep:console-subscriber", "tokio/tracing"]
|
||||||
profile_app = ["dep:tracing-tracy"]
|
profile_app = ["dep:tracing-tracy"]
|
||||||
|
|
||||||
@@ -58,11 +63,15 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
|||||||
global_counter = "0.2.2"
|
global_counter = "0.2.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
xkbcommon = { version = "0.6.0", default-features = false, optional = true }
|
xkbcommon = { version = "0.7.0", default-features = false, optional = true }
|
||||||
ctrlc = "3.4.1"
|
ctrlc = "3.4.1"
|
||||||
libc = "0.2.148"
|
libc = "0.2.148"
|
||||||
input-event-codes = "5.16.8"
|
input-event-codes = "5.16.8"
|
||||||
drm-fourcc = { version = "2.2.0", features = ["serde"] }
|
nix = "0.27.1"
|
||||||
|
wayland-scanner = "0.31.0"
|
||||||
|
wayland-backend = "0.3.2"
|
||||||
|
cluFlock = "1.2.7"
|
||||||
|
fxtypemap = "0.2.0"
|
||||||
|
|
||||||
[dependencies.smithay]
|
[dependencies.smithay]
|
||||||
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||||
@@ -94,7 +103,9 @@ optional = true
|
|||||||
|
|
||||||
[dependencies.stardust-xr]
|
[dependencies.stardust-xr]
|
||||||
git = "https://github.com/StardustXR/core.git"
|
git = "https://github.com/StardustXR/core.git"
|
||||||
branch = "camera-item"
|
|
||||||
|
[dependencies.stardust-xr-server-codegen]
|
||||||
|
path = "codegen"
|
||||||
|
|
||||||
# [patch.crates-io.stereokit]
|
# [patch.crates-io.stereokit]
|
||||||
# path = "../stereokit-rs"
|
# path = "../stereokit-rs"
|
||||||
|
|||||||
17
codegen/Cargo.toml
Normal file
17
codegen/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "stardust-xr-server-codegen"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
convert_case = "0.6.0"
|
||||||
|
quote = "1.0.33"
|
||||||
|
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"
|
||||||
563
codegen/src/lib.rs
Normal file
563
codegen/src/lib.rs
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use split_iter::Splittable;
|
||||||
|
use stardust_xr_schemas::protocol::*;
|
||||||
|
|
||||||
|
fn fold_tokens(a: TokenStream, b: TokenStream) -> TokenStream {
|
||||||
|
quote!(#a #b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[proc_macro]
|
||||||
|
// pub fn codegen_root_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
// codegen_protocol(ROOT_PROTOCOL)
|
||||||
|
// }
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn codegen_node_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
codegen_protocol(NODE_PROTOCOL)
|
||||||
|
}
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn codegen_spatial_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
codegen_protocol(SPATIAL_PROTOCOL)
|
||||||
|
}
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn codegen_field_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn codegen_drawable_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
codegen_protocol(DRAWABLE_PROTOCOL)
|
||||||
|
}
|
||||||
|
// #[proc_macro]
|
||||||
|
// pub fn codegen_input_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
// codegen_protocol(INPUT_PROTOCOL)
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn codegen_protocol(protocol: &'static str) -> proc_macro::TokenStream {
|
||||||
|
let protocol = Protocol::parse(protocol).unwrap();
|
||||||
|
let interface = protocol
|
||||||
|
.interface
|
||||||
|
.map(|p| {
|
||||||
|
let virtual_aspect_name = p.path[1..]
|
||||||
|
.split('/')
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.reduce(|a, b| format!("{a}_{b}"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
+ "_interface";
|
||||||
|
generate_aspect(&Aspect {
|
||||||
|
name: virtual_aspect_name,
|
||||||
|
description: protocol.description.clone(),
|
||||||
|
members: p.members,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
let custom_enums = protocol
|
||||||
|
.custom_enums
|
||||||
|
.iter()
|
||||||
|
.map(generate_custom_enum)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let custom_unions = protocol
|
||||||
|
.custom_unions
|
||||||
|
.iter()
|
||||||
|
.map(generate_custom_union)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let custom_structs = protocol
|
||||||
|
.custom_structs
|
||||||
|
.iter()
|
||||||
|
.map(generate_custom_struct)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let aspects = protocol
|
||||||
|
.aspects
|
||||||
|
.iter()
|
||||||
|
.map(generate_aspect)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.unwrap_or_default();
|
||||||
|
// let nodes = protocol
|
||||||
|
// .nodes
|
||||||
|
// .iter()
|
||||||
|
// .map(generate_node)
|
||||||
|
// .reduce(fold_tokens)
|
||||||
|
// .unwrap_or_default();
|
||||||
|
quote!(#custom_enums #custom_unions #custom_structs #aspects #interface).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_custom_enum(custom_enum: &CustomEnum) -> TokenStream {
|
||||||
|
let name = Ident::new(&custom_enum.name.to_case(Case::Pascal), Span::call_site());
|
||||||
|
let description = &custom_enum.description;
|
||||||
|
|
||||||
|
let argument_decls = custom_enum
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|a| Ident::new(&a.to_case(Case::Pascal), Span::call_site()).to_token_stream())
|
||||||
|
.reduce(|a, b| quote!(#a, #b))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
#[derive(Debug, Clone, Copy, serde_repr::Deserialize_repr, serde_repr::Serialize_repr)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum #name {#argument_decls}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_custom_union(custom_union: &CustomUnion) -> TokenStream {
|
||||||
|
let name = Ident::new(&custom_union.name.to_case(Case::Pascal), Span::call_site());
|
||||||
|
let description = &custom_union.description;
|
||||||
|
|
||||||
|
let option_decls = custom_union
|
||||||
|
.options
|
||||||
|
.iter()
|
||||||
|
.map(generate_union_option)
|
||||||
|
.reduce(|a, b| quote!(#a, #b))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum #name {#option_decls}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_union_option(union_option: &UnionOption) -> TokenStream {
|
||||||
|
let name = union_option
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(|n| n.to_case(Case::Pascal))
|
||||||
|
.unwrap_or_else(|| argument_type_option_name(&union_option._type));
|
||||||
|
let description = union_option
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| quote!(#[doc = #d]))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let identifier = Ident::new(&name, Span::call_site());
|
||||||
|
let _type = generate_argument_type(&union_option._type, false, true);
|
||||||
|
quote! (#description #identifier(#_type))
|
||||||
|
}
|
||||||
|
fn generate_custom_struct(custom_struct: &CustomStruct) -> TokenStream {
|
||||||
|
let name = Ident::new(&custom_struct.name.to_case(Case::Pascal), Span::call_site());
|
||||||
|
let description = &custom_struct.description;
|
||||||
|
|
||||||
|
let argument_decls = custom_struct
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.map(|a| generate_argument_decl(a, true))
|
||||||
|
.map(|d| quote!(pub #d))
|
||||||
|
.reduce(|a, b| quote!(#a, #b))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct #name {#argument_decls}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn generate_node(node: &Node) -> TokenStream {
|
||||||
|
// let node_name = Ident::new(&node.name, Span::call_site());
|
||||||
|
// let description = &node.description;
|
||||||
|
|
||||||
|
// let aspects = node
|
||||||
|
// .aspects
|
||||||
|
// .iter()
|
||||||
|
// .map(|a| {
|
||||||
|
// let aspect_name = Ident::new(&format!("{a}Aspect"), Span::call_site());
|
||||||
|
// quote!(impl #aspect_name for #node_name {})
|
||||||
|
// })
|
||||||
|
// .reduce(fold_tokens)
|
||||||
|
// .unwrap_or_default();
|
||||||
|
|
||||||
|
// quote! {
|
||||||
|
// #[doc = #description]
|
||||||
|
// #[derive(Debug)]
|
||||||
|
// pub struct #node_name (crate::node::Node);
|
||||||
|
// impl crate::node::NodeType for #node_name {
|
||||||
|
// fn node(&self) -> &crate::node::Node {
|
||||||
|
// &self.0
|
||||||
|
// }
|
||||||
|
// fn alias(&self) -> Self {
|
||||||
|
// #node_name(self.0.alias())
|
||||||
|
// }
|
||||||
|
// fn from_path(client: &std::sync::Arc<crate::client::Client>, path: String, destroyable: bool) -> Self {
|
||||||
|
// #node_name(crate::node::Node::from_path(client, path, destroyable))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl serde::Serialize for #node_name {
|
||||||
|
// fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
// let node_path = self.0.get_path().map_err(|e| serde::ser::Error::custom(e))?;
|
||||||
|
// serializer.serialize_str(&node_path)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// #aspects
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn generate_aspect(aspect: &Aspect) -> TokenStream {
|
||||||
|
let description = &aspect.description;
|
||||||
|
let (client_members, server_members) = aspect.members.iter().split(|m| m.side == Side::Server);
|
||||||
|
|
||||||
|
let client_mod_name = Ident::new(
|
||||||
|
&format!("{}_client", &aspect.name.to_case(Case::Snake)),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let client_side_members = client_members
|
||||||
|
.map(generate_member)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.map(|t| {
|
||||||
|
quote! {
|
||||||
|
pub mod #client_mod_name {
|
||||||
|
#t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let aspect_trait_name = Ident::new(
|
||||||
|
&format!("{}Aspect", &aspect.name.to_case(Case::Pascal)),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let server_side_members = server_members
|
||||||
|
.map(generate_member)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let add_node_members = aspect
|
||||||
|
.members
|
||||||
|
.iter()
|
||||||
|
.filter(|m| m.side == Side::Server)
|
||||||
|
.map(generate_handler)
|
||||||
|
.reduce(fold_tokens)
|
||||||
|
.map(|members| {
|
||||||
|
quote! {
|
||||||
|
fn add_node_members(node: &crate::nodes::Node) {
|
||||||
|
#members
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
let server_side_members = quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
pub trait #aspect_trait_name {
|
||||||
|
#add_node_members
|
||||||
|
#server_side_members
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quote!(#client_side_members #server_side_members)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_member(member: &Member) -> TokenStream {
|
||||||
|
let name_str = &member.name;
|
||||||
|
let name = Ident::new(&member.name.to_case(Case::Snake), Span::call_site());
|
||||||
|
let description = &member.description;
|
||||||
|
|
||||||
|
let side = member.side;
|
||||||
|
let _type = member._type;
|
||||||
|
|
||||||
|
let first_args = match member.side {
|
||||||
|
Side::Server => {
|
||||||
|
quote!(_node: std::sync::Arc<crate::nodes::Node>, _calling_client: std::sync::Arc<crate::core::client::Client>)
|
||||||
|
}
|
||||||
|
Side::Client => quote!(_node: &crate::nodes::Node),
|
||||||
|
};
|
||||||
|
let argument_decls = member
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|a| generate_argument_decl(a, member.side == Side::Server))
|
||||||
|
.fold(first_args, |a, b| quote!(#a, #b));
|
||||||
|
|
||||||
|
let argument_uses = member
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|a| generate_argument_serialize(&a.name, &a._type, a.optional))
|
||||||
|
.reduce(|a, b| quote!(#a, #b))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let return_type = member
|
||||||
|
.return_type
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| generate_argument_type(&r, false, true))
|
||||||
|
.unwrap_or_else(|| quote!(()));
|
||||||
|
|
||||||
|
match (side, _type) {
|
||||||
|
(Side::Client, MemberType::Method) => {
|
||||||
|
quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
pub async fn #name(#argument_decls) -> color_eyre::eyre::Result<#return_type> {
|
||||||
|
_node.execute_remote_method(#name_str, &(#argument_uses)).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(#name_str, serialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Side::Server, MemberType::Method) => {
|
||||||
|
quote! {
|
||||||
|
#[doc = #description]
|
||||||
|
fn #name(#argument_decls) -> impl std::future::Future<Output = color_eyre::eyre::Result<#return_type>> + Send + 'static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Side::Server, MemberType::Signal) => {
|
||||||
|
let prefix =
|
||||||
|
if let Some(ArgumentType::Node { _type, return_info }) = &member.return_type {
|
||||||
|
if let Some(return_info) = return_info {
|
||||||
|
let parent_name = Ident::new(
|
||||||
|
&(name_str.to_case(Case::ScreamingSnake) + "_PARENT_PATH"),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let parent_path = &return_info.parent;
|
||||||
|
quote!(const #parent_name: &'static str = #parent_path;)
|
||||||
|
} else {
|
||||||
|
TokenStream::default()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::default()
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
#prefix
|
||||||
|
#[doc = #description]
|
||||||
|
fn #name(#argument_decls) -> color_eyre::eyre::Result<()>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_handler(member: &Member) -> TokenStream {
|
||||||
|
let member_name = &member.name;
|
||||||
|
let member_name_ident = Ident::new(&member_name, Span::call_site());
|
||||||
|
|
||||||
|
let argument_names = member
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(generate_argument_name)
|
||||||
|
.reduce(|a, b| quote!(#a, #b));
|
||||||
|
let argument_types = member
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|a| {
|
||||||
|
let _type = convert_deserializeable_argument_type(&a._type);
|
||||||
|
generate_argument_type(&_type, a.optional, true)
|
||||||
|
})
|
||||||
|
.reduce(|a, b| quote!(#a, #b));
|
||||||
|
// dbg!(&argument_types);
|
||||||
|
let deserialize = argument_names
|
||||||
|
.clone()
|
||||||
|
.zip(argument_types)
|
||||||
|
.map(|(argument_names, argument_types)| {
|
||||||
|
quote!(let (#argument_names): (#argument_types) = stardust_xr::schemas::flex::deserialize(_message.as_ref())?;)
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
let argument_uses = member
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|a| generate_argument_deserialize(&a.name, &a._type, a.optional))
|
||||||
|
.reduce(|a, b| quote!(#a, #b))
|
||||||
|
.unwrap_or_default();
|
||||||
|
match member._type {
|
||||||
|
MemberType::Signal => quote! {
|
||||||
|
node.add_local_signal(#member_name, |_node, _calling_client, _message| {
|
||||||
|
#deserialize
|
||||||
|
Self::#member_name_ident(_node, _calling_client.clone(), #argument_uses)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
MemberType::Method => quote! {
|
||||||
|
node.add_local_method(#member_name, |_node, _calling_client, _message, _method_response| {
|
||||||
|
_method_response.wrap_async(async move {
|
||||||
|
#deserialize
|
||||||
|
Ok((Self::#member_name_ident(_node, _calling_client.clone(), #argument_uses).await?, Vec::new()))
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_argument_name(argument: &Argument) -> TokenStream {
|
||||||
|
Ident::new(&argument.name.to_case(Case::Snake), Span::call_site()).to_token_stream()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_deserializeable_argument_type(argument_type: &ArgumentType) -> ArgumentType {
|
||||||
|
match argument_type {
|
||||||
|
ArgumentType::Node { .. } => ArgumentType::String,
|
||||||
|
f => f.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_argument_deserialize(
|
||||||
|
argument_name: &str,
|
||||||
|
argument_type: &ArgumentType,
|
||||||
|
optional: bool,
|
||||||
|
) -> TokenStream {
|
||||||
|
let name = Ident::new(&argument_name.to_case(Case::Snake), Span::call_site());
|
||||||
|
|
||||||
|
match argument_type {
|
||||||
|
ArgumentType::Node { .. } => match optional {
|
||||||
|
true => quote!(#name.map(|n| _calling_client.get_node(#argument_name, &n)?)),
|
||||||
|
false => quote!(_calling_client.get_node(#argument_name, &#name)?),
|
||||||
|
},
|
||||||
|
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<_>>>()?)
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_argument_serialize(
|
||||||
|
argument_name: &str,
|
||||||
|
argument_type: &ArgumentType,
|
||||||
|
optional: bool,
|
||||||
|
) -> TokenStream {
|
||||||
|
let name = Ident::new(&argument_name.to_case(Case::Snake), Span::call_site());
|
||||||
|
match argument_type {
|
||||||
|
ArgumentType::Node {
|
||||||
|
_type,
|
||||||
|
return_info: _,
|
||||||
|
} => match optional {
|
||||||
|
true => quote!(#name.map(|n| n.get_path())),
|
||||||
|
false => quote!(#name.get_path()),
|
||||||
|
},
|
||||||
|
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<_>>>()?)
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_argument_decl(argument: &Argument, owned_values: bool) -> TokenStream {
|
||||||
|
let name = Ident::new(&argument.name.to_case(Case::Snake), Span::call_site());
|
||||||
|
let mut _type = generate_argument_type(&argument._type, argument.optional, owned_values);
|
||||||
|
quote!(#name: #_type)
|
||||||
|
}
|
||||||
|
fn argument_type_option_name(argument_type: &ArgumentType) -> String {
|
||||||
|
match argument_type {
|
||||||
|
ArgumentType::Bool => "Bool".to_string(),
|
||||||
|
ArgumentType::Int => "Int".to_string(),
|
||||||
|
ArgumentType::UInt => "UInt".to_string(),
|
||||||
|
ArgumentType::Float => "Float".to_string(),
|
||||||
|
ArgumentType::Vec2 => "Vec2".to_string(),
|
||||||
|
ArgumentType::Vec3 => "Vec3".to_string(),
|
||||||
|
ArgumentType::Quat => "Quat".to_string(),
|
||||||
|
ArgumentType::Color => "Color".to_string(),
|
||||||
|
ArgumentType::String => "String".to_string(),
|
||||||
|
ArgumentType::Bytes => "Bytes".to_string(),
|
||||||
|
ArgumentType::Vec(v) => format!("{}Vector", argument_type_option_name(&v)),
|
||||||
|
ArgumentType::Map(m) => format!("{}Map", argument_type_option_name(&m)),
|
||||||
|
ArgumentType::Datamap => "Datamap".to_string(),
|
||||||
|
ArgumentType::ResourceID => "ResourceID".to_string(),
|
||||||
|
ArgumentType::Enum(e) => e.clone(),
|
||||||
|
ArgumentType::Union(u) => u.clone(),
|
||||||
|
ArgumentType::Struct(s) => s.clone(),
|
||||||
|
ArgumentType::Node { _type, .. } => _type.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_argument_type(
|
||||||
|
argument_type: &ArgumentType,
|
||||||
|
optional: bool,
|
||||||
|
owned: bool,
|
||||||
|
) -> TokenStream {
|
||||||
|
let _type = match argument_type {
|
||||||
|
ArgumentType::Bool => quote!(bool),
|
||||||
|
ArgumentType::Int => quote!(i32),
|
||||||
|
ArgumentType::UInt => quote!(u32),
|
||||||
|
ArgumentType::Float => quote!(f32),
|
||||||
|
ArgumentType::Vec2 => quote!(mint::Vector2<f32>),
|
||||||
|
ArgumentType::Vec3 => quote!(mint::Vector3<f32>),
|
||||||
|
ArgumentType::Quat => quote!(mint::Quaternion<f32>),
|
||||||
|
ArgumentType::Color => quote!(stardust_xr::values::Color),
|
||||||
|
ArgumentType::Bytes => {
|
||||||
|
if !owned {
|
||||||
|
quote!(&[u8])
|
||||||
|
} else {
|
||||||
|
quote!(Vec<u8>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::String => {
|
||||||
|
if !owned {
|
||||||
|
quote!(&str)
|
||||||
|
} else {
|
||||||
|
quote!(String)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::Vec(t) => {
|
||||||
|
let t = generate_argument_type(&t, false, true);
|
||||||
|
if !owned {
|
||||||
|
quote!(&[#t])
|
||||||
|
} else {
|
||||||
|
quote!(Vec<#t>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::Map(t) => {
|
||||||
|
let t = generate_argument_type(&t, false, true);
|
||||||
|
|
||||||
|
if !owned {
|
||||||
|
quote!(&rustc_hash::FxHashMap<String, #t>)
|
||||||
|
} else {
|
||||||
|
quote!(rustc_hash::FxHashMap<String, #t>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::Datamap => {
|
||||||
|
if !owned {
|
||||||
|
quote!(&stardust_xr::values::Datamap)
|
||||||
|
} else {
|
||||||
|
quote!(stardust_xr::values::Datamap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::ResourceID => {
|
||||||
|
if !owned {
|
||||||
|
quote!(&stardust_xr::values::ResourceID)
|
||||||
|
} else {
|
||||||
|
quote!(stardust_xr::values::ResourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::Enum(e) => {
|
||||||
|
let enum_name = Ident::new(&e.to_case(Case::Pascal), Span::call_site());
|
||||||
|
quote!(#enum_name)
|
||||||
|
}
|
||||||
|
ArgumentType::Union(u) => {
|
||||||
|
let union_name = Ident::new(&u.to_case(Case::Pascal), Span::call_site());
|
||||||
|
quote!(#union_name)
|
||||||
|
}
|
||||||
|
ArgumentType::Struct(s) => {
|
||||||
|
let struct_name = Ident::new(&s.to_case(Case::Pascal), Span::call_site());
|
||||||
|
if !owned {
|
||||||
|
quote!(&#struct_name)
|
||||||
|
} else {
|
||||||
|
quote!(#struct_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgumentType::Node {
|
||||||
|
_type,
|
||||||
|
return_info: _,
|
||||||
|
} => {
|
||||||
|
if !owned {
|
||||||
|
quote!(&std::sync::Arc<crate::nodes::Node>)
|
||||||
|
} else {
|
||||||
|
quote!(std::sync::Arc<crate::nodes::Node>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if optional {
|
||||||
|
quote!(Option<#_type>)
|
||||||
|
} else {
|
||||||
|
_type
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
effects = let
|
effects = let
|
||||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||||
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
||||||
in { branch, rev, ... }: {
|
in { ref, rev, ... }: {
|
||||||
gnome-graphical-test = hci-effects.mkEffect {
|
gnome-graphical-test = hci-effects.mkEffect {
|
||||||
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
||||||
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
||||||
${pkgs.discord-sh}/bin/discord.sh \
|
${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\`" \
|
--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 "Branch;${branch}" \
|
--field "Ref;${ref}" \
|
||||||
--field "Commit ID;${rev}" \
|
--field "Commit ID;${rev}" \
|
||||||
--field "Flatland Revision;${flatland.rev}" \
|
--field "Flatland Revision;${flatland.rev}" \
|
||||||
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
use std::ffi::c_void;
|
|
||||||
|
|
||||||
use color_eyre::eyre::{ensure, Result};
|
|
||||||
use smithay::backend::{egl::EGLContext, renderer::gles::GlesRenderer};
|
|
||||||
use stereokit as sk;
|
|
||||||
|
|
||||||
struct EGLRawHandles {
|
|
||||||
display: *const c_void,
|
|
||||||
config: *const c_void,
|
|
||||||
context: *const c_void,
|
|
||||||
}
|
|
||||||
fn get_sk_egl() -> Result<EGLRawHandles> {
|
|
||||||
ensure!(
|
|
||||||
unsafe { sk::sys::backend_graphics_get() }
|
|
||||||
== sk::sys::backend_graphics__backend_graphics_opengles_egl,
|
|
||||||
"StereoKit is not running using EGL!"
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(unsafe {
|
|
||||||
EGLRawHandles {
|
|
||||||
display: sk::sys::backend_opengl_egl_get_display() as *const c_void,
|
|
||||||
config: sk::sys::backend_opengl_egl_get_config() as *const c_void,
|
|
||||||
context: sk::sys::backend_opengl_egl_get_context() as *const c_void,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BufferManager {
|
|
||||||
pub renderer: GlesRenderer,
|
|
||||||
}
|
|
||||||
impl BufferManager {
|
|
||||||
pub fn new() -> Result<Self> {
|
|
||||||
let egl_raw_handles = get_sk_egl()?;
|
|
||||||
let renderer = unsafe {
|
|
||||||
GlesRenderer::new(EGLContext::from_raw(
|
|
||||||
egl_raw_handles.display,
|
|
||||||
egl_raw_handles.config,
|
|
||||||
egl_raw_handles.context,
|
|
||||||
)?)?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(BufferManager { renderer })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_context_current(&self) {
|
|
||||||
unsafe {
|
|
||||||
let _ = self.renderer.egl_context().make_current();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
client_state::{ClientState, CLIENT_STATES},
|
client_state::{ClientState, CLIENT_STATES},
|
||||||
|
destroy_queue,
|
||||||
scenegraph::Scenegraph,
|
scenegraph::Scenegraph,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -209,7 +210,9 @@ impl Client {
|
|||||||
if let Some(flush_join_handle) = self.flush_join_handle.get() {
|
if let Some(flush_join_handle) = self.flush_join_handle.get() {
|
||||||
flush_join_handle.abort();
|
flush_join_handle.abort();
|
||||||
}
|
}
|
||||||
CLIENTS.remove(self);
|
if let Some(client) = CLIENTS.remove(self) {
|
||||||
|
destroy_queue::add(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl ClientState {
|
|||||||
}
|
}
|
||||||
fn spatial_transform(client: &Client, path: &str) -> Option<Mat4> {
|
fn spatial_transform(client: &Client, path: &str) -> Option<Mat4> {
|
||||||
let node = client.scenegraph.get_node(path)?;
|
let node = client.scenegraph.get_node(path)?;
|
||||||
let spatial = node.spatial.get()?;
|
let spatial = node.get_aspect::<Spatial>().ok()?;
|
||||||
Some(spatial.global_transform())
|
Some(spatial.global_transform())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +78,10 @@ impl ClientState {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
(k.clone(), {
|
(k.clone(), {
|
||||||
let node = Node::create(client, "/spatial/anchor", k, true)
|
let node = Node::create_parent_name(client, "/spatial/anchor", k, true)
|
||||||
.add_to_scenegraph()
|
.add_to_scenegraph()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Spatial::add_to(&node, None, *v, false).unwrap();
|
Spatial::add_to(&node, None, *v, false);
|
||||||
k.clone()
|
k.clone()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use tokio::sync::mpsc::{self, unbounded_channel};
|
||||||
|
|
||||||
static MAIN_DESTROY_QUEUE: Mutex<Vec<Box<dyn Any + Send + Sync>>> = Mutex::new(Vec::new());
|
static MAIN_DESTROY_QUEUE: Lazy<(
|
||||||
|
mpsc::UnboundedSender<Box<dyn Any + Send + Sync>>,
|
||||||
|
Mutex<mpsc::UnboundedReceiver<Box<dyn Any + Send + Sync>>>,
|
||||||
|
)> = Lazy::new(|| {
|
||||||
|
let (tx, rx) = unbounded_channel();
|
||||||
|
(tx, Mutex::new(rx))
|
||||||
|
});
|
||||||
|
|
||||||
pub fn add<T: Any + Sync + Send>(thing: T) {
|
pub fn add<T: Any + Sync + Send>(thing: T) {
|
||||||
MAIN_DESTROY_QUEUE.lock().push(Box::new(thing));
|
MAIN_DESTROY_QUEUE.0.send(Box::new(thing)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear() {
|
pub fn clear() {
|
||||||
MAIN_DESTROY_QUEUE.lock().clear();
|
while let Ok(thing) = MAIN_DESTROY_QUEUE.1.lock().try_recv() {
|
||||||
|
drop(thing)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/core/idl_utils.rs
Normal file
11
src/core/idl_utils.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! create_interface {
|
||||||
|
($iface:ident, $aspect:ident, $path:expr) => {
|
||||||
|
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
||||||
|
let node = Node::create_path(client, $path, false);
|
||||||
|
<$iface as $aspect>::add_node_members(&node);
|
||||||
|
node.add_to_scenegraph()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,10 +3,9 @@ pub mod client_state;
|
|||||||
pub mod delta;
|
pub mod delta;
|
||||||
pub mod destroy_queue;
|
pub mod destroy_queue;
|
||||||
pub mod eventloop;
|
pub mod eventloop;
|
||||||
|
pub mod idl_utils;
|
||||||
pub mod node_collections;
|
pub mod node_collections;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
pub mod scenegraph;
|
pub mod scenegraph;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
pub mod typed_datamap;
|
|
||||||
pub mod buffers;
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use rustc_hash::FxHashMap;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Registry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Weak<T>>>>);
|
pub struct Registry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Weak<T>>>>);
|
||||||
|
|
||||||
impl<T: Send + Sync + ?Sized> Registry<T> {
|
impl<T: Send + Sync + ?Sized> Registry<T> {
|
||||||
@@ -56,7 +57,9 @@ impl<T: Send + Sync + ?Sized> Registry<T> {
|
|||||||
}
|
}
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
let registry = self.0.lock();
|
let registry = self.0.lock();
|
||||||
let Some(registry) = &*registry else {return true};
|
let Some(registry) = &*registry else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
if registry.is_empty() {
|
if registry.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -68,6 +71,11 @@ impl<T: Send + Sync + ?Sized> Clone for Registry<T> {
|
|||||||
Self(Mutex::new(self.0.lock().clone()))
|
Self(Mutex::new(self.0.lock().clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<T: Send + Sync + ?Sized> Default for Registry<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct OwnedRegistry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Arc<T>>>>);
|
pub struct OwnedRegistry<T: Send + Sync + ?Sized>(Mutex<Option<FxHashMap<usize, Arc<T>>>>);
|
||||||
|
|
||||||
@@ -98,9 +106,12 @@ impl<T: Send + Sync + ?Sized> OwnedRegistry<T> {
|
|||||||
self.lock()
|
self.lock()
|
||||||
.contains_key(&(ptr::addr_of!(*t) as *const () as usize))
|
.contains_key(&(ptr::addr_of!(*t) as *const () as usize))
|
||||||
}
|
}
|
||||||
pub fn remove(&self, t: &T) {
|
pub fn remove(&self, t: &T) -> Option<Arc<T>>
|
||||||
|
where
|
||||||
|
T: Sized,
|
||||||
|
{
|
||||||
self.lock()
|
self.lock()
|
||||||
.remove(&(ptr::addr_of!(*t) as *const () as usize));
|
.remove(&(ptr::addr_of!(*t) as *const () as usize))
|
||||||
}
|
}
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.lock().clear();
|
self.lock().clear();
|
||||||
|
|||||||
@@ -1,83 +1,46 @@
|
|||||||
use color_eyre::eyre::eyre;
|
use stardust_xr::values::ResourceID;
|
||||||
use serde::{de::Visitor, Deserialize};
|
|
||||||
use std::{ffi::OsStr, path::PathBuf};
|
use std::{ffi::OsStr, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
use super::client::Client;
|
||||||
pub enum ResourceID {
|
|
||||||
File(PathBuf),
|
|
||||||
Namespaced { namespace: String, path: PathBuf },
|
|
||||||
}
|
|
||||||
impl ResourceID {
|
|
||||||
pub fn get_file(&self, prefixes: &[PathBuf], extensions: &[&OsStr]) -> Option<PathBuf> {
|
|
||||||
match self {
|
|
||||||
ResourceID::File(file) => (file.is_absolute()
|
|
||||||
&& file.exists() && Self::has_extension(file, extensions))
|
|
||||||
.then_some(file.clone()),
|
|
||||||
ResourceID::Namespaced { namespace, path } => {
|
|
||||||
let file_name = path.file_name()?;
|
|
||||||
prefixes
|
|
||||||
.iter()
|
|
||||||
.filter_map(|prefix| {
|
|
||||||
let prefixed_path = prefix.clone().join(namespace).join(path);
|
|
||||||
let parent = prefixed_path.parent()?;
|
|
||||||
std::fs::read_dir(parent).ok()
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.filter_map(|item| item.ok())
|
|
||||||
.map(|dir_entry| dir_entry.path())
|
|
||||||
.filter(|path| path.file_stem() == Some(file_name))
|
|
||||||
.find(|path| Self::has_extension(path, extensions))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_extension(path: &PathBuf, extensions: &[&OsStr]) -> bool {
|
lazy_static::lazy_static! {
|
||||||
if let Some(path_extension) = path.extension() {
|
static ref THEMES: Vec<PathBuf> = std::env::var("STARDUST_THEMES").map(|s| s.split(":").map(PathBuf::from).collect()).unwrap_or_default();
|
||||||
extensions.contains(&path_extension)
|
}
|
||||||
} else {
|
|
||||||
false
|
fn has_extension(path: &PathBuf, extensions: &[&OsStr]) -> bool {
|
||||||
|
if let Some(path_extension) = path.extension() {
|
||||||
|
extensions.contains(&path_extension)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_resource_file(
|
||||||
|
resource: &ResourceID,
|
||||||
|
client: &Client,
|
||||||
|
extensions: &[&OsStr],
|
||||||
|
) -> Option<PathBuf> {
|
||||||
|
match resource {
|
||||||
|
ResourceID::Direct(file) => {
|
||||||
|
(file.is_absolute() && file.exists() && has_extension(file, extensions))
|
||||||
|
.then_some(file.clone())
|
||||||
|
}
|
||||||
|
ResourceID::Namespaced { namespace, path } => {
|
||||||
|
let file_name = path.file_name()?;
|
||||||
|
let base_prefixes = client.base_resource_prefixes.lock().clone();
|
||||||
|
THEMES
|
||||||
|
.iter()
|
||||||
|
.chain(base_prefixes.iter())
|
||||||
|
.filter_map(|prefix| {
|
||||||
|
let prefixed_path = prefix.clone().join(namespace).join(path);
|
||||||
|
let parent = prefixed_path.parent()?;
|
||||||
|
std::fs::read_dir(parent).ok()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.filter_map(|item| item.ok())
|
||||||
|
.map(|dir_entry| dir_entry.path())
|
||||||
|
.filter(|path| path.file_stem() == Some(file_name))
|
||||||
|
.find(|path| has_extension(path, extensions))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'de> Deserialize<'de> for ResourceID {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
deserializer.deserialize_any(ResourceVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ResourceVisitor;
|
|
||||||
impl<'de> Visitor<'de> for ResourceVisitor {
|
|
||||||
type Value = ResourceID;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("A string containing an absolute path to file or \"[namespace]:[path]\" for a namespaced resource")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(if v.starts_with('/') {
|
|
||||||
let path = PathBuf::from(v);
|
|
||||||
path.metadata().map_err(serde::de::Error::custom)?;
|
|
||||||
ResourceID::File(path)
|
|
||||||
} else if let Some((namespace, path)) = v.split_once(':') {
|
|
||||||
ResourceID::Namespaced {
|
|
||||||
namespace: namespace.to_string(),
|
|
||||||
path: PathBuf::from(path),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(serde::de::Error::custom(eyre!("Invalid format for string")));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_str(&v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::nodes::alias::Alias;
|
||||||
use crate::nodes::Node;
|
use crate::nodes::Node;
|
||||||
use crate::{core::client::Client, nodes::Message};
|
use crate::{core::client::Client, nodes::Message};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
@@ -5,8 +6,11 @@ use once_cell::sync::OnceCell;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::Ordering;
|
use portable_atomic::Ordering;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use serde::Serialize;
|
||||||
use stardust_xr::scenegraph;
|
use stardust_xr::scenegraph;
|
||||||
use stardust_xr::scenegraph::ScenegraphError;
|
use stardust_xr::scenegraph::ScenegraphError;
|
||||||
|
use stardust_xr::schemas::flex::serialize;
|
||||||
|
use std::future::Future;
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
@@ -36,8 +40,8 @@ impl Scenegraph {
|
|||||||
|
|
||||||
pub fn get_node(&self, path: &str) -> Option<Arc<Node>> {
|
pub fn get_node(&self, path: &str) -> Option<Arc<Node>> {
|
||||||
let mut node = self.nodes.lock().get(path)?.clone();
|
let mut node = self.nodes.lock().get(path)?.clone();
|
||||||
while let Some(alias) = node.alias.get() {
|
while let Ok(alias) = node.get_aspect::<Alias>() {
|
||||||
if alias.enabled.load(Ordering::Relaxed) {
|
if alias.enabled.load(Ordering::Acquire) {
|
||||||
node = alias.original.upgrade()?;
|
node = alias.original.upgrade()?;
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
@@ -57,11 +61,35 @@ impl MethodResponseSender {
|
|||||||
pub fn send(self, t: Result<Message, ScenegraphError>) {
|
pub fn send(self, t: Result<Message, ScenegraphError>) {
|
||||||
let _ = self.0.send(t.map(|m| (m.data, m.fds)));
|
let _ = self.0.send(t.map(|m| (m.data, m.fds)));
|
||||||
}
|
}
|
||||||
|
// pub fn send_method_return<T: Serialize>(
|
||||||
|
// self,
|
||||||
|
// result: color_eyre::eyre::Result<(T, Vec<OwnedFd>)>,
|
||||||
|
// ) {
|
||||||
|
// let _ = self.0.send(map_method_return(result));
|
||||||
|
// }
|
||||||
pub fn wrap_sync<F: FnOnce() -> color_eyre::eyre::Result<Message>>(self, f: F) {
|
pub fn wrap_sync<F: FnOnce() -> color_eyre::eyre::Result<Message>>(self, f: F) {
|
||||||
self.send(f().map_err(|e| ScenegraphError::MethodError {
|
self.send(f().map_err(|e| ScenegraphError::MethodError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
pub fn wrap_async<T: Serialize>(
|
||||||
|
self,
|
||||||
|
f: impl Future<Output = color_eyre::eyre::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<(Vec<u8>, Vec<OwnedFd>), ScenegraphError> {
|
||||||
|
let (value, fds) = result.map_err(|e| ScenegraphError::MethodError {
|
||||||
|
error: e.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let serialized_value = serialize(value).map_err(|e| ScenegraphError::MethodError {
|
||||||
|
error: format!("Internal: Serialization failed: {e}"),
|
||||||
|
})?;
|
||||||
|
Ok((serialized_value, fds))
|
||||||
}
|
}
|
||||||
impl scenegraph::Scenegraph for Scenegraph {
|
impl scenegraph::Scenegraph for Scenegraph {
|
||||||
fn send_signal(
|
fn send_signal(
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
use stardust_xr::schemas::{
|
|
||||||
flat::Datamap,
|
|
||||||
flex::flexbuffers::{FlexbufferSerializer, Reader, ReaderError},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::nodes::Message;
|
|
||||||
|
|
||||||
pub struct TypedDatamap<T: DeserializeOwned + Serialize>(T);
|
|
||||||
impl<T: DeserializeOwned + Serialize> TypedDatamap<T> {
|
|
||||||
pub fn new(data: T) -> Self {
|
|
||||||
TypedDatamap(data)
|
|
||||||
}
|
|
||||||
pub fn from_flex(message: Message) -> Result<Self> {
|
|
||||||
let root = Reader::get_root(message.as_ref())?;
|
|
||||||
T::deserialize(root).map(Self::new).map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
pub fn to_datamap(&mut self) -> Result<Datamap> {
|
|
||||||
let mut serializer = FlexbufferSerializer::default();
|
|
||||||
self.0.serialize(&mut serializer)?;
|
|
||||||
Datamap::new(serializer.take_buffer()).map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
pub fn serialize(&mut self) -> Option<Vec<u8>> {
|
|
||||||
let mut serializer = FlexbufferSerializer::default();
|
|
||||||
self.0.serialize(&mut serializer).ok()?;
|
|
||||||
// check if this is actually a map
|
|
||||||
Reader::get_root(serializer.view()).ok()?.get_map().ok()?;
|
|
||||||
Some(serializer.take_buffer())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: DeserializeOwned + Serialize> Default for TypedDatamap<T>
|
|
||||||
where
|
|
||||||
T: Default,
|
|
||||||
{
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(T::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: DeserializeOwned + Serialize> Deref for TypedDatamap<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: DeserializeOwned + Serialize> DerefMut for TypedDatamap<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
27
src/main.rs
27
src/main.rs
@@ -6,7 +6,7 @@ mod wayland;
|
|||||||
|
|
||||||
use crate::core::client::CLIENTS;
|
use crate::core::client::CLIENTS;
|
||||||
use crate::core::client_state::ClientState;
|
use crate::core::client_state::ClientState;
|
||||||
use crate::core::{destroy_queue, buffers};
|
use crate::core::destroy_queue;
|
||||||
use crate::nodes::items::camera;
|
use crate::nodes::items::camera;
|
||||||
use crate::nodes::{audio, drawable, hmd, input};
|
use crate::nodes::{audio, drawable, hmd, input};
|
||||||
use crate::objects::input::eye_pointer::EyePointer;
|
use crate::objects::input::eye_pointer::EyePointer;
|
||||||
@@ -14,12 +14,12 @@ use crate::objects::input::mouse_pointer::MousePointer;
|
|||||||
use crate::objects::input::sk_controller::SkController;
|
use crate::objects::input::sk_controller::SkController;
|
||||||
use crate::objects::input::sk_hand::SkHand;
|
use crate::objects::input::sk_hand::SkHand;
|
||||||
use crate::objects::play_space::PlaySpace;
|
use crate::objects::play_space::PlaySpace;
|
||||||
|
use crate::wayland::X_DISPLAY;
|
||||||
|
|
||||||
use self::core::eventloop::EventLoop;
|
use self::core::eventloop::EventLoop;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use smithay::reexports::nix;
|
|
||||||
use stardust_xr::server;
|
use stardust_xr::server;
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -119,6 +119,7 @@ fn main() {
|
|||||||
overlay_app: cli_args.overlay_priority.is_some(),
|
overlay_app: cli_args.overlay_priority.is_some(),
|
||||||
overlay_priority: cli_args.overlay_priority.unwrap_or(u32::MAX),
|
overlay_priority: cli_args.overlay_priority.unwrap_or(u32::MAX),
|
||||||
disable_desktop_input_window: true,
|
disable_desktop_input_window: true,
|
||||||
|
render_scaling: 2.0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.init()
|
.init()
|
||||||
@@ -126,6 +127,8 @@ fn main() {
|
|||||||
let _ = SK_MULTITHREAD.set(sk.multithreaded());
|
let _ = SK_MULTITHREAD.set(sk.multithreaded());
|
||||||
info!("Init StereoKit");
|
info!("Init StereoKit");
|
||||||
|
|
||||||
|
sk.render_set_multisample(0);
|
||||||
|
|
||||||
sk.material_set_shader(
|
sk.material_set_shader(
|
||||||
sk.material_find("default/material_pbr").unwrap(),
|
sk.material_find("default/material_pbr").unwrap(),
|
||||||
sk.shader_find("default/shader_pbr_clip").unwrap(),
|
sk.shader_find("default/shader_pbr_clip").unwrap(),
|
||||||
@@ -199,10 +202,8 @@ fn main() {
|
|||||||
let event_loop_info = info_receiver.blocking_recv().unwrap();
|
let event_loop_info = info_receiver.blocking_recv().unwrap();
|
||||||
let _tokio_handle = event_loop_info.tokio_handle.enter();
|
let _tokio_handle = event_loop_info.tokio_handle.enter();
|
||||||
|
|
||||||
let mut buffer_manager = buffers::BufferManager::new().expect("Could not initialize buffer manager");
|
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
let mut wayland = wayland::Wayland::new(&buffer_manager).expect("Could not initialize wayland");
|
let mut wayland = wayland::Wayland::new().expect("Could not initialize wayland");
|
||||||
info!("Stardust ready!");
|
info!("Stardust ready!");
|
||||||
|
|
||||||
let mut startup_child = (|| {
|
let mut startup_child = (|| {
|
||||||
@@ -235,8 +236,10 @@ fn main() {
|
|||||||
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
||||||
startup_command.env("WAYLAND_DISPLAY", &wayland_socket);
|
startup_command.env("WAYLAND_DISPLAY", &wayland_socket);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "xwayland")]
|
startup_command.env(
|
||||||
startup_command.env("DISPLAY", format!(":{}", wayland.xwayland_state.display));
|
"DISPLAY",
|
||||||
|
format!(":{}", X_DISPLAY.get().cloned().unwrap_or_default()),
|
||||||
|
);
|
||||||
startup_command.env("GDK_BACKEND", "wayland");
|
startup_command.env("GDK_BACKEND", "wayland");
|
||||||
startup_command.env("QT_QPA_PLATFORM", "wayland");
|
startup_command.env("QT_QPA_PLATFORM", "wayland");
|
||||||
startup_command.env("MOZ_ENABLE_WAYLAND", "1");
|
startup_command.env("MOZ_ENABLE_WAYLAND", "1");
|
||||||
@@ -262,8 +265,8 @@ fn main() {
|
|||||||
let _span = debug_span!("StereoKit step");
|
let _span = debug_span!("StereoKit step");
|
||||||
let _span = _span.enter();
|
let _span = _span.enter();
|
||||||
|
|
||||||
camera::send_rendered();
|
|
||||||
hmd::frame(sk);
|
hmd::frame(sk);
|
||||||
|
camera::update(sk);
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
wayland.frame_event(sk);
|
wayland.frame_event(sk);
|
||||||
destroy_queue::clear();
|
destroy_queue::clear();
|
||||||
@@ -295,13 +298,11 @@ fn main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
wayland.update(sk, &mut buffer_manager);
|
wayland.update(sk);
|
||||||
drawable::draw(sk);
|
drawable::draw(sk);
|
||||||
audio::update(sk);
|
audio::update(sk);
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
buffer_manager.make_context_current();
|
wayland.make_context_current();
|
||||||
|
|
||||||
camera::update(sk, &mut buffer_manager);
|
|
||||||
},
|
},
|
||||||
|_sk| {
|
|_sk| {
|
||||||
info!("Cleanly shut down StereoKit");
|
info!("Cleanly shut down StereoKit");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::Node;
|
use super::{Aspect, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use portable_atomic::AtomicBool;
|
use portable_atomic::AtomicBool;
|
||||||
@@ -35,7 +35,7 @@ impl Alias {
|
|||||||
"Node already exists"
|
"Node already exists"
|
||||||
);
|
);
|
||||||
|
|
||||||
let node = Node::create(client, parent, name, true).add_to_scenegraph()?;
|
let node = Node::create_parent_name(client, parent, name, true).add_to_scenegraph()?;
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
enabled: Arc::new(AtomicBool::new(true)),
|
enabled: Arc::new(AtomicBool::new(true)),
|
||||||
node: Arc::downgrade(&node),
|
node: Arc::downgrade(&node),
|
||||||
@@ -43,7 +43,10 @@ impl Alias {
|
|||||||
info,
|
info,
|
||||||
};
|
};
|
||||||
let alias = original.aliases.add(alias);
|
let alias = original.aliases.add(alias);
|
||||||
let _ = node.alias.set(alias);
|
node.add_aspect_raw(alias);
|
||||||
Ok(node)
|
Ok(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for Alias {
|
||||||
|
const NAME: &'static str = "Alias";
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
use super::{Message, Node};
|
use super::{Aspect, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::destroy_queue;
|
use crate::core::destroy_queue;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::resource::ResourceID;
|
use crate::core::resource::get_resource_file;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::create_interface;
|
||||||
use color_eyre::eyre::{ensure, eyre, Result};
|
use crate::nodes::spatial::{Spatial, Transform};
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use glam::{vec3, Vec4Swizzles};
|
use glam::{vec3, Vec4Swizzles};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use send_wrapper::SendWrapper;
|
use send_wrapper::SendWrapper;
|
||||||
use serde::Deserialize;
|
use stardust_xr::values::ResourceID;
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
use std::{ffi::OsStr, path::PathBuf};
|
||||||
use stereokit::{Sound as SkSound, SoundInstance, StereoKitDraw};
|
use stereokit::{Sound as SkSound, SoundInstance, StereoKitDraw};
|
||||||
|
|
||||||
static SOUND_REGISTRY: Registry<Sound> = Registry::new();
|
static SOUND_REGISTRY: Registry<Sound> = Registry::new();
|
||||||
|
|
||||||
|
stardust_xr_server_codegen::codegen_audio_protocol!();
|
||||||
pub struct Sound {
|
pub struct Sound {
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
|
|
||||||
@@ -28,26 +30,16 @@ pub struct Sound {
|
|||||||
stop: Mutex<Option<()>>,
|
stop: Mutex<Option<()>>,
|
||||||
play: Mutex<Option<()>>,
|
play: Mutex<Option<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sound {
|
impl Sound {
|
||||||
pub fn add_to(node: &Arc<Node>, resource_id: ResourceID) -> Result<Arc<Sound>> {
|
pub fn add_to(node: &Arc<Node>, resource_id: ResourceID) -> Result<Arc<Sound>> {
|
||||||
ensure!(
|
let pending_audio_path = get_resource_file(
|
||||||
node.spatial.get().is_some(),
|
&resource_id,
|
||||||
"Internal: Node does not have a spatial attached!"
|
&*node.get_client().ok_or_else(|| eyre!("Client not found"))?,
|
||||||
);
|
&[OsStr::new("wav"), OsStr::new("mp3")],
|
||||||
let pending_audio_path = resource_id
|
)
|
||||||
.get_file(
|
.ok_or_else(|| eyre!("Resource not found"))?;
|
||||||
&node
|
|
||||||
.get_client()
|
|
||||||
.ok_or_else(|| eyre!("Client not found"))?
|
|
||||||
.base_resource_prefixes
|
|
||||||
.lock()
|
|
||||||
.clone(),
|
|
||||||
&[OsStr::new("wav"), OsStr::new("mp3")],
|
|
||||||
)
|
|
||||||
.ok_or_else(|| eyre!("Resource not found"))?;
|
|
||||||
let sound = Sound {
|
let sound = Sound {
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
pending_audio_path,
|
pending_audio_path,
|
||||||
sk_sound: OnceCell::new(),
|
sk_sound: OnceCell::new(),
|
||||||
@@ -56,9 +48,8 @@ impl Sound {
|
|||||||
play: Mutex::new(None),
|
play: Mutex::new(None),
|
||||||
};
|
};
|
||||||
let sound_arc = SOUND_REGISTRY.add(sound);
|
let sound_arc = SOUND_REGISTRY.add(sound);
|
||||||
node.add_local_signal("play", Sound::play_flex);
|
node.add_aspect_raw(sound_arc.clone());
|
||||||
node.add_local_signal("stop", Sound::stop_flex);
|
<Sound as SoundAspect>::add_node_members(node);
|
||||||
let _ = node.sound.set(sound_arc.clone());
|
|
||||||
Ok(sound_arc)
|
Ok(sound_arc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +62,7 @@ impl Sound {
|
|||||||
sk.sound_inst_stop(instance);
|
sk.sound_inst_stop(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.play.lock().is_some() && self.instance.lock().is_none() {
|
if self.instance.lock().is_none() && self.play.lock().take().is_some() {
|
||||||
self.instance.lock().replace(sk.sound_play(
|
self.instance.lock().replace(sk.sound_play(
|
||||||
sound.as_ref(),
|
sound.as_ref(),
|
||||||
vec3(0.0, 0.0, 0.0),
|
vec3(0.0, 0.0, 0.0),
|
||||||
@@ -82,19 +73,30 @@ impl Sound {
|
|||||||
sk.sound_inst_set_pos(*instance, self.space.global_transform().w_axis.xyz());
|
sk.sound_inst_set_pos(*instance, self.space.global_transform().w_axis.xyz());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn play_flex(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
impl Aspect for Sound {
|
||||||
let sound = node.sound.get().unwrap();
|
const NAME: &'static str = "Sound";
|
||||||
|
}
|
||||||
|
impl SoundAspect for Sound {
|
||||||
|
fn play(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
|
let sound = node.get_aspect::<Sound>().unwrap();
|
||||||
sound.play.lock().replace(());
|
sound.play.lock().replace(());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
fn stop(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
pub fn stop_flex(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
let sound = node.get_aspect::<Sound>().unwrap();
|
||||||
let sound = node.sound.get().unwrap();
|
|
||||||
sound.stop.lock().replace(());
|
sound.stop.lock().replace(());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Drop for Sound {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(sk_sound) = self.sk_sound.take() {
|
||||||
|
destroy_queue::add(sk_sound);
|
||||||
|
}
|
||||||
|
SOUND_REGISTRY.remove(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(sk: &impl StereoKitDraw) {
|
pub fn update(sk: &impl StereoKitDraw) {
|
||||||
for sound in SOUND_REGISTRY.get_valid_contents() {
|
for sound in SOUND_REGISTRY.get_valid_contents() {
|
||||||
@@ -102,35 +104,25 @@ pub fn update(sk: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
create_interface!(AudioInterface, AudioInterfaceAspect, "/audio");
|
||||||
let node = Node::create(client, "", "audio", false);
|
struct AudioInterface;
|
||||||
node.add_local_signal("create_sound", create_flex);
|
impl AudioInterfaceAspect for AudioInterface {
|
||||||
node.add_to_scenegraph().map(|_| ())
|
#[doc = "Create a sound node. WAV and MP3 are supported."]
|
||||||
}
|
fn create_sound(
|
||||||
|
_node: Arc<Node>,
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
calling_client: Arc<Client>,
|
||||||
#[derive(Deserialize)]
|
name: String,
|
||||||
struct CreateSoundInfo<'a> {
|
parent: Arc<Node>,
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
resource: ResourceID,
|
resource: ResourceID,
|
||||||
}
|
) -> Result<()> {
|
||||||
let info: CreateSoundInfo = deserialize(message.as_ref())?;
|
let node =
|
||||||
let node = Node::create(&calling_client, "/audio/sound", info.name, true);
|
Node::create_parent_name(&calling_client, Self::CREATE_SOUND_PARENT_PATH, &name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = transform.to_mat4(true, true, true);
|
||||||
let node = node.add_to_scenegraph()?;
|
let node = node.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
Sound::add_to(&node, info.resource)?;
|
Sound::add_to(&node, resource)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Sound {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(instance) = self.instance.lock().take() {
|
|
||||||
destroy_queue::add(instance);
|
|
||||||
}
|
|
||||||
SOUND_REGISTRY.remove(self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
use super::alias::AliasInfo;
|
use super::alias::AliasInfo;
|
||||||
use super::fields::Field;
|
use super::fields::Field;
|
||||||
use super::spatial::{parse_transform, Spatial};
|
use super::spatial::{parse_transform, Spatial};
|
||||||
use super::{Alias, Message, Node};
|
use super::{Alias, Aspect, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::node_collections::LifeLinkedNodeMap;
|
use crate::core::node_collections::LifeLinkedNodeMap;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
use crate::create_interface;
|
||||||
use crate::nodes::fields::{find_field, FIELD_ALIAS_INFO};
|
use crate::nodes::fields::FIELD_ALIAS_INFO;
|
||||||
use crate::nodes::spatial::find_spatial_parent;
|
use crate::nodes::spatial::Transform;
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
||||||
use glam::vec3a;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mint::{Quaternion, Vector3};
|
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use stardust_xr::schemas::flex::flexbuffers;
|
||||||
use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize};
|
use stardust_xr::values::Datamap;
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::fmt::Display;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -28,11 +24,17 @@ lazy_static! {
|
|||||||
static PULSE_SENDER_REGISTRY: Registry<PulseSender> = Registry::new();
|
static PULSE_SENDER_REGISTRY: Registry<PulseSender> = Registry::new();
|
||||||
pub static PULSE_RECEIVER_REGISTRY: Registry<PulseReceiver> = Registry::new();
|
pub static PULSE_RECEIVER_REGISTRY: Registry<PulseReceiver> = Registry::new();
|
||||||
|
|
||||||
pub fn mask_matches(mask_map_lesser: &Mask, mask_map_greater: &Mask) -> bool {
|
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<_> {
|
(|| -> Result<_> {
|
||||||
for key in mask_map_lesser.get_mask()?.iter_keys() {
|
for key in get_mask(mask_map_lesser)?.iter_keys() {
|
||||||
let lesser_key = mask_map_lesser.get_mask()?.index(key)?;
|
let lesser_key = get_mask(mask_map_lesser)?.index(key)?;
|
||||||
let greater_key = mask_map_greater.get_mask()?.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
|
// otherwise zero-length vectors don't count the same as a single type vector
|
||||||
if lesser_key.flexbuffer_type().is_heterogenous_vector()
|
if lesser_key.flexbuffer_type().is_heterogenous_vector()
|
||||||
&& lesser_key.as_vector().len() == 0
|
&& lesser_key.as_vector().len() == 0
|
||||||
@@ -51,57 +53,24 @@ pub fn mask_matches(mask_map_lesser: &Mask, mask_map_greater: &Mask) -> bool {
|
|||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Mask(pub Vec<u8>);
|
stardust_xr_server_codegen::codegen_data_protocol!();
|
||||||
impl Mask {
|
|
||||||
pub fn from_struct<T: Default + Serialize>() -> Self {
|
|
||||||
let mut serializer = flexbuffers::FlexbufferSerializer::new();
|
|
||||||
T::default().serialize(&mut serializer).unwrap();
|
|
||||||
Mask(serializer.take_buffer())
|
|
||||||
}
|
|
||||||
pub fn get_mask(&self) -> Result<flexbuffers::MapReader<&[u8]>> {
|
|
||||||
flexbuffers::Reader::get_root(self.0.as_slice())
|
|
||||||
.map_err(|_| eyre!("Mask is not a valid flexbuffer"))?
|
|
||||||
.get_map()
|
|
||||||
.map_err(|_| eyre!("Mask is not a valid map"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Mask {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
flexbuffers::Reader::get_root(self.0.as_slice())
|
|
||||||
.unwrap()
|
|
||||||
.to_string()
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct SendDataInfo<'a> {
|
|
||||||
uid: &'a str,
|
|
||||||
data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PulseSender {
|
pub struct PulseSender {
|
||||||
uid: String,
|
|
||||||
node: Weak<Node>,
|
node: Weak<Node>,
|
||||||
pub mask: Mask,
|
pub mask: Datamap,
|
||||||
aliases: LifeLinkedNodeMap<String>,
|
aliases: LifeLinkedNodeMap<String>,
|
||||||
}
|
}
|
||||||
impl PulseSender {
|
impl PulseSender {
|
||||||
pub fn add_to(node: &Arc<Node>, mask: Mask) -> Result<Arc<PulseSender>> {
|
pub fn add_to(node: &Arc<Node>, mask: Datamap) -> Result<Arc<PulseSender>> {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
|
|
||||||
let sender = PulseSender {
|
let sender = PulseSender {
|
||||||
uid: nanoid!(),
|
|
||||||
node: Arc::downgrade(node),
|
node: Arc::downgrade(node),
|
||||||
mask,
|
mask,
|
||||||
aliases: LifeLinkedNodeMap::default(),
|
aliases: LifeLinkedNodeMap::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// <PulseSender as PulseSenderAspect>::add_node_members(node);
|
||||||
let sender = PULSE_SENDER_REGISTRY.add(sender);
|
let sender = PULSE_SENDER_REGISTRY.add(sender);
|
||||||
let _ = node.pulse_sender.set(sender.clone());
|
node.add_aspect_raw(sender.clone());
|
||||||
node.add_local_signal("send_data", PulseSender::send_data_flex);
|
|
||||||
for receiver in PULSE_RECEIVER_REGISTRY.get_valid_contents() {
|
for receiver in PULSE_RECEIVER_REGISTRY.get_valid_contents() {
|
||||||
sender.handle_new_receiver(&receiver);
|
sender.handle_new_receiver(&receiver);
|
||||||
}
|
}
|
||||||
@@ -111,97 +80,61 @@ impl PulseSender {
|
|||||||
if !mask_matches(&self.mask, &receiver.mask) {
|
if !mask_matches(&self.mask, &receiver.mask) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(tx_node) = self.node.upgrade() else {return};
|
let Some(tx_node) = self.node.upgrade() else {
|
||||||
let Some(tx_client) = tx_node.get_client() else {return};
|
return;
|
||||||
let Some(rx_node) = receiver.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
|
// Receiver itself
|
||||||
let rx_alias = Alias::create(
|
let Ok(rx_alias) = Alias::create(
|
||||||
&tx_client,
|
&tx_client,
|
||||||
tx_node.get_path(),
|
tx_node.get_path(),
|
||||||
receiver.uid.as_str(),
|
receiver.uid.as_str(),
|
||||||
&rx_node,
|
&rx_node,
|
||||||
AliasInfo {
|
AliasInfo {
|
||||||
server_methods: vec!["sendData", "getTransform"],
|
server_methods: vec!["send_data"],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
) else {
|
||||||
if let Ok(rx_alias) = rx_alias {
|
return;
|
||||||
self.aliases.add(receiver.uid.clone(), &rx_alias);
|
|
||||||
|
|
||||||
if let Some(rx_field_node) = receiver.field.spatial_ref().node.upgrade() {
|
|
||||||
// Receiver's field
|
|
||||||
let rx_field_alias = Alias::create(
|
|
||||||
&tx_client,
|
|
||||||
rx_alias.get_path(),
|
|
||||||
"field",
|
|
||||||
&rx_field_node,
|
|
||||||
FIELD_ALIAS_INFO.clone(),
|
|
||||||
);
|
|
||||||
if let Ok(rx_field_alias) = rx_field_alias {
|
|
||||||
self.aliases
|
|
||||||
.add(receiver.uid.clone() + "-field", &rx_field_alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct NewReceiverInfo<'a> {
|
|
||||||
uid: &'a str,
|
|
||||||
distance: f32,
|
|
||||||
position: Vector3<f32>,
|
|
||||||
rotation: Quaternion<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let (_, rotation, position) = Spatial::space_to_space_matrix(
|
|
||||||
rx_node.spatial.get().map(|s| s.as_ref()),
|
|
||||||
tx_node.spatial.get().map(|s| s.as_ref()),
|
|
||||||
)
|
|
||||||
.to_scale_rotation_translation();
|
|
||||||
|
|
||||||
let info = NewReceiverInfo {
|
|
||||||
uid: &receiver.uid,
|
|
||||||
distance: receiver
|
|
||||||
.field
|
|
||||||
.distance(tx_node.spatial.get().unwrap(), vec3a(0.0, 0.0, 0.0)),
|
|
||||||
position: position.into(),
|
|
||||||
rotation: rotation.into(),
|
|
||||||
};
|
};
|
||||||
|
self.aliases.add(receiver.uid.clone(), &rx_alias);
|
||||||
|
|
||||||
let Ok(data) = serialize(info) else {return};
|
// Receiver's field
|
||||||
let _ = tx_node.send_remote_signal("new_receiver", data);
|
let Ok(rx_field_alias) = Alias::create(
|
||||||
|
&tx_client,
|
||||||
|
rx_alias.get_path(),
|
||||||
|
"field",
|
||||||
|
&rx_node.get_aspect::<PulseReceiver>().unwrap().field_node,
|
||||||
|
FIELD_ALIAS_INFO.clone(),
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.aliases
|
||||||
|
.add(receiver.uid.clone() + "-field", &rx_field_alias);
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
pulse_sender_client::new_receiver(&tx_node, &receiver.uid, &rx_alias, &rx_field_alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_drop_receiver(&self, receiver: &PulseReceiver) {
|
fn handle_drop_receiver(&self, receiver: &PulseReceiver) {
|
||||||
let uid = receiver.uid.as_str();
|
let uid = receiver.uid.as_str();
|
||||||
self.aliases.remove(uid);
|
self.aliases.remove(uid);
|
||||||
self.aliases.remove(&(uid.to_string() + "-field"));
|
self.aliases.remove(&(uid.to_string() + "-field"));
|
||||||
let Some(tx_node) = self.node.upgrade() else {return};
|
let Some(tx_node) = self.node.upgrade() else {
|
||||||
let Ok(data) = serialize(&uid) else {return};
|
return;
|
||||||
let _ = tx_node.send_remote_signal("drop_receiver", data);
|
};
|
||||||
}
|
let _ = pulse_sender_client::drop_receiver(&tx_node, uid);
|
||||||
|
|
||||||
fn send_data_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
|
||||||
let info: SendDataInfo = deserialize(message.as_ref())?;
|
|
||||||
let receiver_node = calling_client.get_node("Pulse receiver", info.uid)?;
|
|
||||||
let receiver =
|
|
||||||
receiver_node.get_aspect("Pulse Receiver", "pulse receiver", |n| &n.pulse_receiver)?;
|
|
||||||
let receiver_mask = &receiver_node
|
|
||||||
.get_aspect("Pulse receiver", "pulse receiver", |node| {
|
|
||||||
&node.pulse_receiver
|
|
||||||
})?
|
|
||||||
.mask;
|
|
||||||
|
|
||||||
let data_mask = Mask(info.data);
|
|
||||||
data_mask.get_mask()?;
|
|
||||||
ensure!(
|
|
||||||
mask_matches(receiver_mask, &data_mask),
|
|
||||||
"Message ({}) does not contain the same keys as the receiver's mask ({})",
|
|
||||||
data_mask,
|
|
||||||
receiver_mask
|
|
||||||
);
|
|
||||||
receiver.send_data(&node.pulse_sender.get().unwrap().uid, data_mask.0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for PulseSender {
|
||||||
|
const NAME: &'static str = "PulseSender";
|
||||||
|
}
|
||||||
|
impl PulseSenderAspect for PulseSender {}
|
||||||
impl Drop for PulseSender {
|
impl Drop for PulseSender {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
PULSE_SENDER_REGISTRY.remove(self);
|
PULSE_SENDER_REGISTRY.remove(self);
|
||||||
@@ -211,39 +144,52 @@ impl Drop for PulseSender {
|
|||||||
pub struct PulseReceiver {
|
pub struct PulseReceiver {
|
||||||
uid: String,
|
uid: String,
|
||||||
pub node: Weak<Node>,
|
pub node: Weak<Node>,
|
||||||
pub field: Arc<Field>,
|
pub field_node: Arc<Node>,
|
||||||
pub mask: Mask,
|
pub mask: Datamap,
|
||||||
}
|
}
|
||||||
impl PulseReceiver {
|
impl PulseReceiver {
|
||||||
pub fn add_to(node: &Arc<Node>, field: Arc<Field>, mask: Mask) -> Result<Arc<PulseReceiver>> {
|
pub fn add_to(
|
||||||
ensure!(
|
node: &Arc<Node>,
|
||||||
node.spatial.get().is_some(),
|
field_node: Arc<Node>,
|
||||||
"Internal: Node does not have a spatial attached!"
|
mask: Datamap,
|
||||||
);
|
) -> Result<Arc<PulseReceiver>> {
|
||||||
|
|
||||||
let receiver = PulseReceiver {
|
let receiver = PulseReceiver {
|
||||||
uid: nanoid!(),
|
uid: nanoid!(),
|
||||||
node: Arc::downgrade(node),
|
node: Arc::downgrade(node),
|
||||||
field,
|
field_node,
|
||||||
mask,
|
mask,
|
||||||
};
|
};
|
||||||
let receiver = PULSE_RECEIVER_REGISTRY.add(receiver);
|
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() {
|
for sender in PULSE_SENDER_REGISTRY.get_valid_contents() {
|
||||||
sender.handle_new_receiver(&receiver);
|
sender.handle_new_receiver(&receiver);
|
||||||
}
|
}
|
||||||
let _ = node.pulse_receiver.set(receiver.clone());
|
|
||||||
Ok(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();
|
||||||
|
|
||||||
pub fn send_data(&self, uid: &str, data: Vec<u8>) -> Result<()> {
|
ensure!(
|
||||||
if let Some(node) = self.node.upgrade() {
|
mask_matches(&this_receiver.mask, &data),
|
||||||
node.send_remote_signal("data", serialize(SendDataInfo { uid, data })?)?;
|
"Message ({data:?}) does not contain the same keys as the receiver's mask ({:?})",
|
||||||
}
|
this_receiver.mask
|
||||||
|
);
|
||||||
|
pulse_receiver_client::data(&node, &sender.uid, &data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for PulseReceiver {
|
impl Drop for PulseReceiver {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
PULSE_RECEIVER_REGISTRY.remove(self);
|
PULSE_RECEIVER_REGISTRY.remove(self);
|
||||||
@@ -253,76 +199,64 @@ impl Drop for PulseReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
create_interface!(DataInterface, DataInterfaceAspect, "/data");
|
||||||
let node = Node::create(client, "", "data", false);
|
struct DataInterface;
|
||||||
node.add_local_signal("create_pulse_sender", create_pulse_sender_flex);
|
impl DataInterfaceAspect for DataInterface {
|
||||||
node.add_local_signal("create_pulse_receiver", create_pulse_receiver_flex);
|
fn create_pulse_sender(
|
||||||
node.add_local_method("register_keymap", register_keymap_flex);
|
_node: Arc<Node>,
|
||||||
node.add_local_method("get_keymap", get_keymap_flex);
|
calling_client: Arc<Client>,
|
||||||
node.add_to_scenegraph().map(|_| ())
|
name: String,
|
||||||
}
|
parent: Arc<Node>,
|
||||||
|
|
||||||
pub fn create_pulse_sender_flex(
|
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreatePulseSenderInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
mask: Vec<u8>,
|
mask: Datamap,
|
||||||
|
) -> Result<()> {
|
||||||
|
get_mask(&mask)?;
|
||||||
|
let node = Node::create_parent_name(
|
||||||
|
&calling_client,
|
||||||
|
Self::CREATE_PULSE_SENDER_PARENT_PATH,
|
||||||
|
&name,
|
||||||
|
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(())
|
||||||
}
|
}
|
||||||
let info: CreatePulseSenderInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/data/sender", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
|
|
||||||
let mask = Mask(info.mask);
|
fn create_pulse_receiver(
|
||||||
mask.get_mask()?;
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
let node = node.add_to_scenegraph()?;
|
name: String,
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
parent: Arc<Node>,
|
||||||
PulseSender::add_to(&node, mask)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_pulse_receiver_flex(
|
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreatePulseReceiverInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
field_path: &'a str,
|
field: Arc<Node>,
|
||||||
mask: Vec<u8>,
|
mask: Datamap,
|
||||||
|
) -> Result<()> {
|
||||||
|
get_mask(&mask)?;
|
||||||
|
let node = Node::create_parent_name(
|
||||||
|
&calling_client,
|
||||||
|
Self::CREATE_PULSE_RECEIVER_PARENT_PATH,
|
||||||
|
&name,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let transform = parse_transform(transform, true, true, false);
|
||||||
|
let _ = 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(())
|
||||||
}
|
}
|
||||||
let info: CreatePulseReceiverInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/data/receiver", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
let field = find_field(&calling_client, info.field_path)?;
|
|
||||||
let mask = Mask(info.mask);
|
|
||||||
mask.get_mask()?;
|
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
async fn register_keymap(
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
_node: Arc<Node>,
|
||||||
PulseReceiver::add_to(&node, field, mask)?;
|
_calling_client: Arc<Client>,
|
||||||
Ok(())
|
keymap: String,
|
||||||
}
|
) -> Result<String> {
|
||||||
|
|
||||||
pub fn register_keymap_flex(
|
|
||||||
_node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let keymap: String = deserialize(message.as_ref())?;
|
|
||||||
let mut keymaps = KEYMAPS.lock();
|
let mut keymaps = KEYMAPS.lock();
|
||||||
if let Some(found_keymap_id) = keymaps
|
if let Some(found_keymap_id) = keymaps
|
||||||
.iter()
|
.iter()
|
||||||
@@ -330,26 +264,25 @@ pub fn register_keymap_flex(
|
|||||||
.map(|(k, _v)| k)
|
.map(|(k, _v)| k)
|
||||||
.last()
|
.last()
|
||||||
{
|
{
|
||||||
return Ok(serialize(found_keymap_id)?.into());
|
return Ok(found_keymap_id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let generated_id = nanoid!();
|
let generated_id = nanoid!();
|
||||||
keymaps.insert(generated_id.clone(), keymap);
|
keymaps.insert(generated_id.clone(), keymap);
|
||||||
|
|
||||||
Ok(serialize(generated_id)?.into())
|
Ok(generated_id)
|
||||||
});
|
}
|
||||||
}
|
|
||||||
pub fn get_keymap_flex(
|
|
||||||
_node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let keymap_id: &str = deserialize(message.as_ref())?;
|
|
||||||
let keymaps = KEYMAPS.lock();
|
|
||||||
let Some(keymap) = keymaps.get(keymap_id) else {bail!("Could not find keymap. Try registering it")};
|
|
||||||
|
|
||||||
Ok(serialize(keymap)?.into())
|
async fn get_keymap(
|
||||||
});
|
_node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
keymap_id: String,
|
||||||
|
) -> Result<String> {
|
||||||
|
let keymaps = KEYMAPS.lock();
|
||||||
|
let Some(keymap) = keymaps.get(&keymap_id) else {
|
||||||
|
bail!("Could not find keymap. Try registering it")
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(keymap.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,48 @@
|
|||||||
|
use super::{Line, LinesAspect};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, registry::Registry},
|
core::{client::Client, registry::Registry},
|
||||||
nodes::{
|
nodes::{spatial::Spatial, Aspect, Node},
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
|
||||||
Message, Node,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, ensure, Result};
|
use color_eyre::eyre::Result;
|
||||||
use glam::Vec3A;
|
use glam::Vec3A;
|
||||||
use mint::Vector3;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::{AtomicBool, Ordering};
|
use portable_atomic::{AtomicBool, Ordering};
|
||||||
use prisma::{Flatten, Lerp, Rgba};
|
use prisma::Lerp;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::{schemas::flex::deserialize, values::Transform};
|
|
||||||
use std::{collections::VecDeque, sync::Arc};
|
use std::{collections::VecDeque, sync::Arc};
|
||||||
use stereokit::{bounds_grow_to_fit_pt, Bounds, Color128, LinePoint as SkLinePoint, StereoKitDraw};
|
use stereokit::{bounds_grow_to_fit_pt, Bounds, Color128, LinePoint as SkLinePoint, StereoKitDraw};
|
||||||
|
|
||||||
use super::Drawable;
|
|
||||||
|
|
||||||
static LINES_REGISTRY: Registry<Lines> = Registry::new();
|
static LINES_REGISTRY: Registry<Lines> = Registry::new();
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
struct LinePointRaw {
|
|
||||||
point: Vector3<f32>,
|
|
||||||
thickness: f32,
|
|
||||||
color: [f32; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct LineData {
|
|
||||||
points: Vec<LinePointRaw>,
|
|
||||||
cyclic: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Lines {
|
pub struct Lines {
|
||||||
enabled: Arc<AtomicBool>,
|
enabled: Arc<AtomicBool>,
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
data: Mutex<LineData>,
|
data: Mutex<Vec<Line>>,
|
||||||
}
|
}
|
||||||
impl Lines {
|
impl Lines {
|
||||||
fn add_to(node: &Arc<Node>, points: Vec<LinePointRaw>, cyclic: bool) -> Result<Arc<Lines>> {
|
pub fn add_to(node: &Arc<Node>, lines: Vec<Line>) -> Result<Arc<Lines>> {
|
||||||
ensure!(
|
let _ = node
|
||||||
node.drawable.get().is_none(),
|
.get_aspect::<Spatial>()
|
||||||
"Internal: Node already has a drawable attached!"
|
.unwrap()
|
||||||
);
|
.bounding_box_calc
|
||||||
|
.set(|node| {
|
||||||
let _ = node.spatial.get().unwrap().bounding_box_calc.set(|node| {
|
let mut bounds = Bounds::default();
|
||||||
let mut bounds = Bounds::default();
|
if let Ok(lines) = node.get_aspect::<Lines>() {
|
||||||
let Some(Drawable::Lines(lines)) = node.drawable.get() else {return bounds};
|
for line in &*lines.data.lock() {
|
||||||
for point in &lines.data.lock().points {
|
for point in &line.points {
|
||||||
bounds = bounds_grow_to_fit_pt(bounds, point.point);
|
bounds = bounds_grow_to_fit_pt(bounds, point.point);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
bounds
|
}
|
||||||
});
|
bounds
|
||||||
|
});
|
||||||
|
|
||||||
let lines = LINES_REGISTRY.add(Lines {
|
let lines = LINES_REGISTRY.add(Lines {
|
||||||
enabled: node.enabled.clone(),
|
enabled: node.enabled.clone(),
|
||||||
space: node.get_aspect("Lines", "spatial", |n| &n.spatial)?.clone(),
|
space: node.get_aspect::<Spatial>()?.clone(),
|
||||||
data: Mutex::new(LineData { points, cyclic }),
|
data: Mutex::new(lines),
|
||||||
});
|
});
|
||||||
node.add_local_signal("set_points", Lines::set_points_flex);
|
<Lines as LinesAspect>::add_node_members(node);
|
||||||
node.add_local_signal("set_cyclic", Lines::set_cyclic_flex);
|
node.add_aspect_raw(lines.clone());
|
||||||
let _ = node.drawable.set(Drawable::Lines(lines.clone()));
|
|
||||||
|
|
||||||
Ok(lines)
|
Ok(lines)
|
||||||
}
|
}
|
||||||
@@ -70,57 +50,55 @@ impl Lines {
|
|||||||
fn draw(&self, draw_ctx: &impl StereoKitDraw) {
|
fn draw(&self, draw_ctx: &impl StereoKitDraw) {
|
||||||
let transform_mat = self.space.global_transform();
|
let transform_mat = self.space.global_transform();
|
||||||
let data = self.data.lock().clone();
|
let data = self.data.lock().clone();
|
||||||
let mut points: VecDeque<SkLinePoint> = data
|
for line in &data {
|
||||||
.points
|
let mut points: VecDeque<SkLinePoint> = line
|
||||||
.iter()
|
.points
|
||||||
.map(|p| SkLinePoint {
|
.iter()
|
||||||
pt: transform_mat.transform_point3a(Vec3A::from(p.point)).into(),
|
.map(|p| SkLinePoint {
|
||||||
thickness: p.thickness,
|
pt: transform_mat.transform_point3a(Vec3A::from(p.point)).into(),
|
||||||
color: p.color.map(|c| (c * 255.0) as u8).into(),
|
thickness: p.thickness,
|
||||||
})
|
color: stereokit::sys::color128::from([
|
||||||
.collect();
|
p.color.c.r,
|
||||||
if data.cyclic && !points.is_empty() {
|
p.color.c.g,
|
||||||
let first = data.points.first().unwrap();
|
p.color.c.b,
|
||||||
let last = data.points.last().unwrap();
|
p.color.a,
|
||||||
let color = Rgba::from_slice(&first.color).lerp(&Rgba::from_slice(&last.color), 0.5);
|
])
|
||||||
let connect_point = SkLinePoint {
|
|
||||||
pt: transform_mat
|
|
||||||
.transform_point3a(Vec3A::from(first.point).lerp(Vec3A::from(last.point), 0.5))
|
|
||||||
.into(),
|
.into(),
|
||||||
thickness: (first.thickness + last.thickness) * 0.5,
|
})
|
||||||
color: Color128::from([color.red(), color.green(), color.blue(), color.alpha()])
|
.collect();
|
||||||
.into(),
|
if line.cyclic && !points.is_empty() {
|
||||||
};
|
let first = line.points.first().unwrap();
|
||||||
points.push_front(connect_point.clone());
|
let last = line.points.last().unwrap();
|
||||||
points.push_back(connect_point);
|
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
let connect_point = SkLinePoint {
|
||||||
|
pt: transform_mat
|
||||||
|
.transform_point3a(
|
||||||
|
Vec3A::from(first.point).lerp(Vec3A::from(last.point), 0.5),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
thickness: (first.thickness + last.thickness) * 0.5,
|
||||||
|
color: color.into(),
|
||||||
|
};
|
||||||
|
points.push_front(connect_point.clone());
|
||||||
|
points.push_back(connect_point);
|
||||||
|
}
|
||||||
|
draw_ctx.line_add_listv(points.make_contiguous());
|
||||||
}
|
}
|
||||||
draw_ctx.line_add_listv(points.make_contiguous());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn set_points_flex(
|
impl Aspect for Lines {
|
||||||
node: &Node,
|
const NAME: &'static str = "Lines";
|
||||||
_calling_client: Arc<Client>,
|
}
|
||||||
message: Message,
|
impl LinesAspect for Lines {
|
||||||
) -> Result<()> {
|
fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> {
|
||||||
let Some(Drawable::Lines(lines)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let lines_aspect = node.get_aspect::<Lines>()?;
|
||||||
|
*lines_aspect.data.lock() = lines;
|
||||||
let mut points: Vec<LinePointRaw> = deserialize(message.as_ref())?;
|
|
||||||
for p in &mut points {
|
|
||||||
p.color[0] = p.color[0].powf(2.2);
|
|
||||||
p.color[1] = p.color[1].powf(2.2);
|
|
||||||
p.color[2] = p.color[2].powf(2.2);
|
|
||||||
}
|
|
||||||
lines.data.lock().points = points;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn set_cyclic_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(Drawable::Lines(lines)) = node.drawable.get() else {bail!("Not a drawable??")};
|
|
||||||
|
|
||||||
lines.data.lock().cyclic = deserialize(message.as_ref())?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,29 +115,3 @@ pub fn draw_all(draw_ctx: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateTextInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
points: Vec<LinePointRaw>,
|
|
||||||
cyclic: bool,
|
|
||||||
}
|
|
||||||
let mut info: CreateTextInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/drawable/lines", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
|
||||||
|
|
||||||
for p in &mut info.points {
|
|
||||||
p.color[0] = p.color[0].powf(2.2);
|
|
||||||
p.color[1] = p.color[1].powf(2.2);
|
|
||||||
p.color[2] = p.color[2].powf(2.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
Lines::add_to(&node, info.points, info.cyclic)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,39 +3,22 @@ pub mod model;
|
|||||||
pub mod shaders;
|
pub mod shaders;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
use self::{
|
use self::{lines::Lines, model::Model, text::Text};
|
||||||
lines::Lines,
|
use super::{
|
||||||
model::{Model, ModelPart},
|
spatial::{Spatial, Transform},
|
||||||
text::Text,
|
Node,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
use super::{Message, Node};
|
core::{client::Client, resource::get_resource_file},
|
||||||
use crate::core::client::Client;
|
create_interface,
|
||||||
use color_eyre::eyre::Result;
|
};
|
||||||
|
use color_eyre::eyre::{self, Result};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Deserialize;
|
use stardust_xr::values::ResourceID;
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
||||||
use std::{path::PathBuf, sync::Arc};
|
|
||||||
use stereokit::StereoKitDraw;
|
use stereokit::StereoKitDraw;
|
||||||
use tracing::instrument;
|
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
// #[instrument(level = "debug", skip(sk))]
|
||||||
let node = Node::create(client, "", "drawable", false);
|
|
||||||
node.add_local_signal("create_lines", lines::create_flex);
|
|
||||||
node.add_local_signal("create_model", model::create_flex);
|
|
||||||
node.add_local_signal("create_text", text::create_flex);
|
|
||||||
node.add_local_signal("set_sky_file", set_sky_file_flex);
|
|
||||||
node.add_to_scenegraph().map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Drawable {
|
|
||||||
Lines(Arc<Lines>),
|
|
||||||
Model(Arc<Model>),
|
|
||||||
ModelPart(Arc<ModelPart>),
|
|
||||||
Text(Arc<Text>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(sk))]
|
|
||||||
pub fn draw(sk: &impl StereoKitDraw) {
|
pub fn draw(sk: &impl StereoKitDraw) {
|
||||||
lines::draw_all(sk);
|
lines::draw_all(sk);
|
||||||
model::draw_all(sk);
|
model::draw_all(sk);
|
||||||
@@ -56,21 +39,83 @@ pub fn draw(sk: &impl StereoKitDraw) {
|
|||||||
static QUEUED_SKYLIGHT: Mutex<Option<PathBuf>> = Mutex::new(None);
|
static QUEUED_SKYLIGHT: Mutex<Option<PathBuf>> = Mutex::new(None);
|
||||||
static QUEUED_SKYTEX: Mutex<Option<PathBuf>> = Mutex::new(None);
|
static QUEUED_SKYTEX: Mutex<Option<PathBuf>> = Mutex::new(None);
|
||||||
|
|
||||||
fn set_sky_file_flex(_node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
stardust_xr_server_codegen::codegen_drawable_protocol!();
|
||||||
#[derive(Deserialize)]
|
create_interface!(DrawableInterface, DrawableInterfaceAspect, "/drawable");
|
||||||
struct SkyFileInfo {
|
|
||||||
path: PathBuf,
|
pub struct DrawableInterface;
|
||||||
skytex: Option<bool>,
|
impl DrawableInterfaceAspect for DrawableInterface {
|
||||||
skylight: Option<bool>,
|
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")])
|
||||||
let info: SkyFileInfo = deserialize(message.as_ref())?;
|
.ok_or(eyre::eyre!("Could not find resource"))?;
|
||||||
info.path.metadata()?;
|
QUEUED_SKYTEX.lock().replace(resource_path);
|
||||||
if info.skytex.unwrap_or_default() {
|
Ok(())
|
||||||
QUEUED_SKYTEX.lock().replace(info.path.clone());
|
|
||||||
}
|
|
||||||
if info.skylight.unwrap_or_default() {
|
|
||||||
QUEUED_SKYLIGHT.lock().replace(info.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
fn set_sky_light(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
light: ResourceID,
|
||||||
|
) -> Result<()> {
|
||||||
|
let resource_path = get_resource_file(&light, &calling_client, &[OsStr::new("hdr")])
|
||||||
|
.ok_or(eyre::eyre!("Could not find resource"))?;
|
||||||
|
QUEUED_SKYLIGHT.lock().replace(resource_path);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_lines(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
lines: Vec<Line>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let node =
|
||||||
|
Node::create_parent_name(&calling_client, Self::CREATE_LINES_PARENT_PATH, &name, true);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let transform = transform.to_mat4(true, true, true);
|
||||||
|
|
||||||
|
let node = node.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
Lines::add_to(&node, lines)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_model(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
model: ResourceID,
|
||||||
|
) -> Result<()> {
|
||||||
|
let node =
|
||||||
|
Node::create_parent_name(&calling_client, Self::LOAD_MODEL_PARENT_PATH, &name, true);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let transform = transform.to_mat4(true, true, true);
|
||||||
|
let node = node.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
Model::add_to(&node, model)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_text(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
) -> Result<()> {
|
||||||
|
let node =
|
||||||
|
Node::create_parent_name(&calling_client, Self::CREATE_TEXT_PARENT_PATH, &name, true);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let transform = transform.to_mat4(true, true, true);
|
||||||
|
|
||||||
|
let node = node.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
Text::add_to(&node, text, style)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,31 @@
|
|||||||
use super::Node;
|
use super::{MaterialParameter, ModelAspect, ModelPartAspect, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
|
use crate::core::destroy_queue;
|
||||||
use crate::core::node_collections::LifeLinkedNodeMap;
|
use crate::core::node_collections::LifeLinkedNodeMap;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::resource::ResourceID;
|
use crate::core::resource::get_resource_file;
|
||||||
use crate::nodes::drawable::Drawable;
|
use crate::nodes::spatial::Spatial;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::Aspect;
|
||||||
use crate::nodes::Message;
|
|
||||||
use crate::SK_MULTITHREAD;
|
use crate::SK_MULTITHREAD;
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use glam::Mat4;
|
|
||||||
use mint::{ColumnMatrix4, Vector2, Vector3, Vector4};
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::{AtomicBool, Ordering};
|
use portable_atomic::{AtomicBool, Ordering};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
use stardust_xr::values::ResourceID;
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use stereokit::named_colors::WHITE;
|
use stereokit::named_colors::WHITE;
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
Bounds, Color128, Material, Model as SKModel, RenderLayer, Shader, StereoKitDraw,
|
Bounds, Color128, Material, Model as SKModel, RenderLayer, Shader, StereoKitDraw,
|
||||||
StereoKitMultiThread,
|
StereoKitMultiThread, Transparency,
|
||||||
};
|
};
|
||||||
|
|
||||||
static MODEL_REGISTRY: Registry<Model> = Registry::new();
|
static MODEL_REGISTRY: Registry<Model> = Registry::new();
|
||||||
|
static HOLDOUT_MATERIAL: OnceCell<Arc<Material>> = OnceCell::new();
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
#[serde(tag = "t", content = "c")]
|
|
||||||
pub enum MaterialParameter {
|
|
||||||
Float(f32),
|
|
||||||
Vector2(Vector2<f32>),
|
|
||||||
Vector3(Vector3<f32>),
|
|
||||||
Vector4(Vector4<f32>),
|
|
||||||
Color([f32; 4]),
|
|
||||||
Int(i32),
|
|
||||||
Int2(Vector2<i32>),
|
|
||||||
Int3(Vector3<i32>),
|
|
||||||
Int4(Vector4<i32>),
|
|
||||||
Bool(bool),
|
|
||||||
UInt(u32),
|
|
||||||
UInt2(Vector2<u32>),
|
|
||||||
UInt3(Vector3<u32>),
|
|
||||||
UInt4(Vector4<u32>),
|
|
||||||
Matrix(ColumnMatrix4<f32>),
|
|
||||||
Texture(ResourceID),
|
|
||||||
}
|
|
||||||
impl MaterialParameter {
|
impl MaterialParameter {
|
||||||
fn apply_to_material(
|
fn apply_to_material(
|
||||||
&self,
|
&self,
|
||||||
@@ -57,56 +35,37 @@ impl MaterialParameter {
|
|||||||
parameter_name: &str,
|
parameter_name: &str,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
MaterialParameter::Float(val) => {
|
MaterialParameter::Bool(val) => {
|
||||||
sk.material_set_float(material, parameter_name, *val);
|
sk.material_set_bool(material, parameter_name, *val);
|
||||||
}
|
|
||||||
MaterialParameter::Vector2(val) => {
|
|
||||||
sk.material_set_vector2(material, parameter_name, *val);
|
|
||||||
}
|
|
||||||
MaterialParameter::Vector3(val) => {
|
|
||||||
sk.material_set_vector3(material, parameter_name, *val);
|
|
||||||
}
|
|
||||||
MaterialParameter::Vector4(val) => {
|
|
||||||
sk.material_set_vector4(material, parameter_name, *val);
|
|
||||||
}
|
|
||||||
MaterialParameter::Color(val) => {
|
|
||||||
sk.material_set_color(material, parameter_name, Color128::from(val.clone()));
|
|
||||||
}
|
}
|
||||||
MaterialParameter::Int(val) => {
|
MaterialParameter::Int(val) => {
|
||||||
sk.material_set_int(material, parameter_name, *val);
|
sk.material_set_int(material, parameter_name, *val);
|
||||||
}
|
}
|
||||||
MaterialParameter::Int2(val) => {
|
|
||||||
sk.material_set_int2(material, parameter_name, val.x, val.y);
|
|
||||||
}
|
|
||||||
MaterialParameter::Int3(val) => {
|
|
||||||
sk.material_set_int3(material, parameter_name, val.x, val.y, val.z);
|
|
||||||
}
|
|
||||||
MaterialParameter::Int4(val) => {
|
|
||||||
sk.material_set_int4(material, parameter_name, val.w, val.x, val.y, val.z);
|
|
||||||
}
|
|
||||||
MaterialParameter::Bool(val) => {
|
|
||||||
sk.material_set_bool(material, parameter_name, *val);
|
|
||||||
}
|
|
||||||
MaterialParameter::UInt(val) => {
|
MaterialParameter::UInt(val) => {
|
||||||
sk.material_set_uint(material, parameter_name, *val);
|
sk.material_set_uint(material, parameter_name, *val);
|
||||||
}
|
}
|
||||||
MaterialParameter::UInt2(val) => {
|
MaterialParameter::Float(val) => {
|
||||||
sk.material_set_uint2(material, parameter_name, val.x, val.y);
|
sk.material_set_float(material, parameter_name, *val);
|
||||||
}
|
}
|
||||||
MaterialParameter::UInt3(val) => {
|
MaterialParameter::Vec2(val) => {
|
||||||
sk.material_set_uint3(material, parameter_name, val.x, val.y, val.z);
|
sk.material_set_vector2(material, parameter_name, *val);
|
||||||
}
|
}
|
||||||
MaterialParameter::UInt4(val) => {
|
MaterialParameter::Vec3(val) => {
|
||||||
sk.material_set_uint4(material, parameter_name, val.w, val.x, val.y, val.z);
|
sk.material_set_vector3(material, parameter_name, *val);
|
||||||
}
|
}
|
||||||
MaterialParameter::Matrix(val) => {
|
MaterialParameter::Color(val) => {
|
||||||
sk.material_set_matrix(material, parameter_name, Mat4::from(*val));
|
sk.material_set_color(
|
||||||
|
material,
|
||||||
|
parameter_name,
|
||||||
|
Color128::new(val.c.r, val.c.g, val.c.b, val.a),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
MaterialParameter::Texture(resource) => {
|
MaterialParameter::Texture(resource) => {
|
||||||
let Some(texture_path) = resource.get_file(
|
let Some(texture_path) =
|
||||||
&client.base_resource_prefixes.lock().clone(),
|
get_resource_file(&resource, &client, &[OsStr::new("png"), OsStr::new("jpg")])
|
||||||
&[OsStr::new("png"), OsStr::new("jpg")],
|
else {
|
||||||
) else {return};
|
return;
|
||||||
|
};
|
||||||
if let Ok(tex) = sk.tex_create_file(texture_path, true, 0) {
|
if let Ok(tex) = sk.tex_create_file(texture_path, true, 0) {
|
||||||
sk.material_set_texture(material, parameter_name, &tex);
|
sk.material_set_texture(material, parameter_name, &tex);
|
||||||
}
|
}
|
||||||
@@ -125,6 +84,22 @@ pub struct ModelPart {
|
|||||||
}
|
}
|
||||||
impl ModelPart {
|
impl ModelPart {
|
||||||
fn create_for_model(sk: &impl StereoKitMultiThread, model: &Arc<Model>, sk_model: &SKModel) {
|
fn create_for_model(sk: &impl StereoKitMultiThread, model: &Arc<Model>, sk_model: &SKModel) {
|
||||||
|
HOLDOUT_MATERIAL.get_or_init(|| {
|
||||||
|
let mat = sk.material_copy(Material::UNLIT);
|
||||||
|
sk.material_set_transparency(&mat, Transparency::None);
|
||||||
|
sk.material_set_color(
|
||||||
|
&mat,
|
||||||
|
"color",
|
||||||
|
stereokit::sys::color128 {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 0.0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Arc::new(mat)
|
||||||
|
});
|
||||||
|
|
||||||
let first_root_part = sk.model_node_get_root(sk_model);
|
let first_root_part = sk.model_node_get_root(sk_model);
|
||||||
let mut current_option_part = Some(first_root_part);
|
let mut current_option_part = Some(first_root_part);
|
||||||
|
|
||||||
@@ -158,40 +133,50 @@ impl ModelPart {
|
|||||||
.and_then(|id| model.parts.get(&id));
|
.and_then(|id| model.parts.get(&id));
|
||||||
let parent_part = parent_node
|
let parent_part = parent_node
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|node| match node.drawable.get() {
|
.and_then(|node| node.get_aspect::<ModelPart>().ok());
|
||||||
Some(Drawable::ModelPart(model_part)) => Some(model_part),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let stardust_model_part = model.space.node()?;
|
let stardust_model_part = model.space.node()?;
|
||||||
let client = stardust_model_part.get_client()?;
|
let client = stardust_model_part.get_client()?;
|
||||||
let mut part_path = parent_part.map(|n| n.path.clone()).unwrap_or_default();
|
let mut part_path = parent_part.map(|n| n.path.clone()).unwrap_or_default();
|
||||||
part_path.push(sk.model_node_get_name(sk_model, id)?);
|
part_path.push(sk.model_node_get_name(sk_model, id)?);
|
||||||
let node = client.scenegraph.add_node(Node::create(
|
let node = client.scenegraph.add_node(Node::create_parent_name(
|
||||||
&client,
|
&client,
|
||||||
stardust_model_part.get_path(),
|
stardust_model_part.get_path(),
|
||||||
part_path.to_str()?,
|
part_path.to_str()?,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let spatial_parent = parent_node
|
let spatial_parent = parent_node
|
||||||
.and_then(|n| n.spatial.get().cloned())
|
.and_then(|n| n.get_aspect::<Spatial>().ok())
|
||||||
.unwrap_or_else(|| model.space.clone());
|
.unwrap_or_else(|| model.space.clone());
|
||||||
let space = Spatial::add_to(
|
let space = Spatial::add_to(
|
||||||
&node,
|
&node,
|
||||||
Some(spatial_parent),
|
Some(spatial_parent),
|
||||||
sk.model_node_get_transform_local(sk_model, id),
|
sk.model_node_get_transform_local(sk_model, id),
|
||||||
false,
|
false,
|
||||||
)
|
);
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
let _ = node.spatial.get().unwrap().bounding_box_calc.set(|node| {
|
let _ = node
|
||||||
let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else {return Bounds::default()};
|
.get_aspect::<Spatial>()
|
||||||
let Some(sk) = SK_MULTITHREAD.get() else {return Bounds::default()};
|
.unwrap()
|
||||||
let Some(model) = model_part.model.upgrade() else {return Bounds::default()};
|
.bounding_box_calc
|
||||||
let Some(sk_model) = model.sk_model.get() else {return Bounds::default()};
|
.set(|node| {
|
||||||
let Some(sk_mesh) = sk.model_node_get_mesh(sk_model, model_part.id) else {return Bounds::default()};
|
let Ok(model_part) = node.get_aspect::<ModelPart>() else {
|
||||||
sk.mesh_get_bounds(sk_mesh)
|
return Bounds::default();
|
||||||
});
|
};
|
||||||
|
let Some(sk) = SK_MULTITHREAD.get() else {
|
||||||
|
return Bounds::default();
|
||||||
|
};
|
||||||
|
let Some(model) = model_part.model.upgrade() else {
|
||||||
|
return Bounds::default();
|
||||||
|
};
|
||||||
|
let Some(sk_model) = model.sk_model.get() else {
|
||||||
|
return Bounds::default();
|
||||||
|
};
|
||||||
|
let Some(sk_mesh) = sk.model_node_get_mesh(sk_model, model_part.id) else {
|
||||||
|
return Bounds::default();
|
||||||
|
};
|
||||||
|
sk.mesh_get_bounds(sk_mesh)
|
||||||
|
});
|
||||||
|
|
||||||
let model_part = Arc::new(ModelPart {
|
let model_part = Arc::new(ModelPart {
|
||||||
id,
|
id,
|
||||||
@@ -201,32 +186,12 @@ impl ModelPart {
|
|||||||
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
||||||
pending_material_replacement: Mutex::new(None),
|
pending_material_replacement: Mutex::new(None),
|
||||||
});
|
});
|
||||||
node.add_local_signal(
|
<ModelPart as ModelPartAspect>::add_node_members(&node);
|
||||||
"set_material_parameter",
|
node.add_aspect_raw(model_part.clone());
|
||||||
ModelPart::set_material_parameter_flex,
|
|
||||||
);
|
|
||||||
let _ = node.drawable.set(Drawable::ModelPart(model_part.clone()));
|
|
||||||
model.parts.add(id, &node);
|
model.parts.add(id, &node);
|
||||||
Some(model_part)
|
Some(model_part)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_material_parameter_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else {bail!("Not a drawable??")};
|
|
||||||
|
|
||||||
let (name, value): (String, MaterialParameter) = deserialize(message.as_ref())?;
|
|
||||||
|
|
||||||
model_part
|
|
||||||
.pending_material_parameters
|
|
||||||
.lock()
|
|
||||||
.insert(name, value);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replace_material(&self, replacement: Arc<Material>) {
|
pub fn replace_material(&self, replacement: Arc<Material>) {
|
||||||
self.pending_material_replacement
|
self.pending_material_replacement
|
||||||
.lock()
|
.lock()
|
||||||
@@ -234,17 +199,27 @@ impl ModelPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, sk: &impl StereoKitDraw) {
|
fn update(&self, sk: &impl StereoKitDraw) {
|
||||||
let Some(model) = self.model.upgrade() else {return};
|
let Some(model) = self.model.upgrade() else {
|
||||||
let Some(sk_model) = model.sk_model.get() else {return};
|
return;
|
||||||
let Some(node) = model.space.node() else {return};
|
};
|
||||||
let Some(client) = node.get_client() else {return};
|
let Some(sk_model) = model.sk_model.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(node) = model.space.node() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(client) = node.get_client() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
|
if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
|
||||||
sk.model_node_set_material(sk_model, self.id, material_replacement.as_ref().as_ref());
|
sk.model_node_set_material(sk_model, self.id, material_replacement.as_ref().as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut material_parameters = self.pending_material_parameters.lock();
|
let mut material_parameters = self.pending_material_parameters.lock();
|
||||||
for (parameter_name, parameter_value) in material_parameters.drain() {
|
for (parameter_name, parameter_value) in material_parameters.drain() {
|
||||||
let Some(material) = sk.model_node_get_material(sk_model, self.id) else {continue};
|
let Some(material) = sk.model_node_get_material(sk_model, self.id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let new_material = sk.material_copy(material);
|
let new_material = sk.material_copy(material);
|
||||||
parameter_value.apply_to_material(&client, sk, &new_material, parameter_name.as_str());
|
parameter_value.apply_to_material(&client, sk, &new_material, parameter_name.as_str());
|
||||||
sk.model_node_set_material(sk_model, self.id, &new_material);
|
sk.model_node_set_material(sk_model, self.id, &new_material);
|
||||||
@@ -257,6 +232,33 @@ impl ModelPart {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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<()> {
|
||||||
|
let model_part = node.get_aspect::<ModelPart>()?;
|
||||||
|
model_part.replace_material(HOLDOUT_MATERIAL.get().unwrap().clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Set the material parameter with `parameter_name` to `value`"]
|
||||||
|
fn set_material_parameter(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
parameter_name: String,
|
||||||
|
value: MaterialParameter,
|
||||||
|
) -> Result<()> {
|
||||||
|
let model_part = node.get_aspect::<ModelPart>()?;
|
||||||
|
model_part
|
||||||
|
.pending_material_parameters
|
||||||
|
.lock()
|
||||||
|
.insert(parameter_name, value);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
self_ref: Weak<Model>,
|
self_ref: Weak<Model>,
|
||||||
@@ -271,31 +273,17 @@ unsafe impl Sync for Model {}
|
|||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
pub fn add_to(node: &Arc<Node>, resource_id: ResourceID) -> Result<Arc<Model>> {
|
pub fn add_to(node: &Arc<Node>, resource_id: ResourceID) -> Result<Arc<Model>> {
|
||||||
ensure!(
|
let pending_model_path = get_resource_file(
|
||||||
node.spatial.get().is_some(),
|
&resource_id,
|
||||||
"Internal: Node does not have a spatial attached!"
|
&*node.get_client().ok_or_else(|| eyre!("Client not found"))?,
|
||||||
);
|
&[OsStr::new("glb"), OsStr::new("gltf")],
|
||||||
ensure!(
|
)
|
||||||
node.drawable.get().is_none(),
|
.ok_or_else(|| eyre!("Resource not found"))?;
|
||||||
"Internal: Node already has a drawable attached!"
|
|
||||||
);
|
|
||||||
|
|
||||||
let pending_model_path = resource_id
|
|
||||||
.get_file(
|
|
||||||
&node
|
|
||||||
.get_client()
|
|
||||||
.ok_or_else(|| eyre!("Client not found"))?
|
|
||||||
.base_resource_prefixes
|
|
||||||
.lock()
|
|
||||||
.clone(),
|
|
||||||
&[OsStr::new("glb"), OsStr::new("gltf")],
|
|
||||||
)
|
|
||||||
.ok_or_else(|| eyre!("Resource not found"))?;
|
|
||||||
|
|
||||||
let model = Arc::new_cyclic(|self_ref| Model {
|
let model = Arc::new_cyclic(|self_ref| Model {
|
||||||
self_ref: self_ref.clone(),
|
self_ref: self_ref.clone(),
|
||||||
enabled: node.enabled.clone(),
|
enabled: node.enabled.clone(),
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
_resource_id: resource_id,
|
_resource_id: resource_id,
|
||||||
sk_model: OnceCell::new(),
|
sk_model: OnceCell::new(),
|
||||||
parts: LifeLinkedNodeMap::default(),
|
parts: LifeLinkedNodeMap::default(),
|
||||||
@@ -308,15 +296,18 @@ impl Model {
|
|||||||
);
|
);
|
||||||
ModelPart::create_for_model(sk, &model.self_ref.upgrade().unwrap(), &sk_model);
|
ModelPart::create_for_model(sk, &model.self_ref.upgrade().unwrap(), &sk_model);
|
||||||
let _ = model.sk_model.set(sk_model);
|
let _ = model.sk_model.set(sk_model);
|
||||||
let _ = node.drawable.set(Drawable::Model(model.clone()));
|
node.add_aspect_raw(model.clone());
|
||||||
Ok(model)
|
Ok(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, sk: &impl StereoKitDraw) {
|
fn draw(&self, sk: &impl StereoKitDraw) {
|
||||||
let Some(sk_model) = self.sk_model.get() else {return};
|
let Some(sk_model) = self.sk_model.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
for model_node_node in self.parts.nodes() {
|
for model_node_node in self.parts.nodes() {
|
||||||
let Some(Drawable::ModelPart(model_node)) = model_node_node.drawable.get() else {continue};
|
if let Ok(model_node) = model_node_node.get_aspect::<ModelPart>() {
|
||||||
model_node.update(sk);
|
model_node.update(sk);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
sk.model_draw(
|
sk.model_draw(
|
||||||
@@ -327,9 +318,15 @@ impl Model {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for Model {
|
||||||
|
const NAME: &'static str = "Model";
|
||||||
|
}
|
||||||
|
impl ModelAspect for Model {}
|
||||||
impl Drop for Model {
|
impl Drop for Model {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
if let Some(sk_model) = self.sk_model.take() {
|
||||||
|
destroy_queue::add(sk_model);
|
||||||
|
}
|
||||||
MODEL_REGISTRY.remove(self);
|
MODEL_REGISTRY.remove(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,21 +338,3 @@ pub fn draw_all(sk: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateModelInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
resource: ResourceID,
|
|
||||||
}
|
|
||||||
let info: CreateModelInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/drawable/model", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
Model::add_to(&node, info.resource)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,107 +1,79 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, destroy_queue, registry::Registry, resource::ResourceID},
|
core::{client::Client, destroy_queue, registry::Registry, resource::get_resource_file},
|
||||||
nodes::{
|
nodes::{spatial::Spatial, Aspect, Node},
|
||||||
drawable::Drawable,
|
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
|
||||||
Message, Node,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use glam::{vec3, Mat4, Vec2};
|
use glam::{vec3, Mat4, Vec2};
|
||||||
use mint::Vector2;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::{AtomicBool, Ordering};
|
use portable_atomic::{AtomicBool, Ordering};
|
||||||
use prisma::{Flatten, Rgba};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::{schemas::flex::deserialize, values::Transform};
|
|
||||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
||||||
use stereokit::{named_colors::WHITE, Color128, StereoKitDraw, TextAlign, TextFit, TextStyle};
|
use stereokit::{
|
||||||
|
named_colors::WHITE, Color128, StereoKitDraw, TextAlign, TextFit, TextStyle as SkTextStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{TextAspect, TextStyle};
|
||||||
|
|
||||||
static TEXT_REGISTRY: Registry<Text> = Registry::new();
|
static TEXT_REGISTRY: Registry<Text> = Registry::new();
|
||||||
|
|
||||||
struct TextData {
|
fn convert_align(x_align: super::XAlign, y_align: super::YAlign) -> TextAlign {
|
||||||
text: String,
|
let x_align = match x_align {
|
||||||
character_height: f32,
|
super::XAlign::Left => TextAlign::XLeft,
|
||||||
text_align: TextAlign,
|
super::XAlign::Center => TextAlign::XCenter,
|
||||||
bounds: Option<Vec2>,
|
super::XAlign::Right => TextAlign::XRight,
|
||||||
fit: TextFit,
|
} as u32;
|
||||||
bounds_align: TextAlign,
|
let y_align = match y_align {
|
||||||
color: Rgba<f32>,
|
super::YAlign::Top => TextAlign::YTop,
|
||||||
|
super::YAlign::Center => TextAlign::YCenter,
|
||||||
|
super::YAlign::Bottom => TextAlign::YBottom,
|
||||||
|
} as u32;
|
||||||
|
|
||||||
|
unsafe { std::mem::transmute(x_align | y_align) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
enabled: Arc<AtomicBool>,
|
enabled: Arc<AtomicBool>,
|
||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
font_path: Option<PathBuf>,
|
font_path: Option<PathBuf>,
|
||||||
style: OnceCell<TextStyle>,
|
style: OnceCell<SkTextStyle>,
|
||||||
|
|
||||||
data: Mutex<TextData>,
|
text: Mutex<String>,
|
||||||
|
data: Mutex<TextStyle>,
|
||||||
}
|
}
|
||||||
impl Text {
|
impl Text {
|
||||||
#[allow(clippy::too_many_arguments)]
|
pub fn add_to(node: &Arc<Node>, text: String, style: TextStyle) -> Result<Arc<Text>> {
|
||||||
pub fn add_to(
|
|
||||||
node: &Arc<Node>,
|
|
||||||
font_resource_id: Option<ResourceID>,
|
|
||||||
text: String,
|
|
||||||
character_height: f32,
|
|
||||||
text_align: TextAlign,
|
|
||||||
bounds: Option<Vector2<f32>>,
|
|
||||||
fit: TextFit,
|
|
||||||
bounds_align: TextAlign,
|
|
||||||
color: Rgba<f32>,
|
|
||||||
) -> Result<Arc<Text>> {
|
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
node.drawable.get().is_none(),
|
|
||||||
"Internal: Node already has a drawable attached!"
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = node.get_client().ok_or_else(|| eyre!("Client not found"))?;
|
let client = node.get_client().ok_or_else(|| eyre!("Client not found"))?;
|
||||||
let text = TEXT_REGISTRY.add(Text {
|
let text = TEXT_REGISTRY.add(Text {
|
||||||
enabled: node.enabled.clone(),
|
enabled: node.enabled.clone(),
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
font_path: font_resource_id.and_then(|res| {
|
font_path: style.font.as_ref().and_then(|res| {
|
||||||
res.get_file(
|
get_resource_file(&res, &client, &[OsStr::new("ttf"), OsStr::new("otf")])
|
||||||
&client.base_resource_prefixes.lock().clone(),
|
|
||||||
&[OsStr::new("ttf"), OsStr::new("otf")],
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
style: OnceCell::new(),
|
style: OnceCell::new(),
|
||||||
|
|
||||||
data: Mutex::new(TextData {
|
text: Mutex::new(text),
|
||||||
text,
|
data: Mutex::new(style),
|
||||||
character_height,
|
|
||||||
text_align,
|
|
||||||
bounds: bounds.map(|b| b.into()),
|
|
||||||
fit,
|
|
||||||
bounds_align,
|
|
||||||
color,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
node.add_local_signal("set_character_height", Text::set_character_height_flex);
|
<Text as TextAspect>::add_node_members(node);
|
||||||
node.add_local_signal("set_text", Text::set_text_flex);
|
node.add_aspect_raw(text.clone());
|
||||||
let _ = node.drawable.set(Drawable::Text(text.clone()));
|
|
||||||
|
|
||||||
Ok(text)
|
Ok(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, sk: &impl StereoKitDraw) {
|
fn draw(&self, sk: &impl StereoKitDraw) {
|
||||||
let style = self
|
let style =
|
||||||
.style
|
self.style
|
||||||
.get_or_try_init(|| -> Result<TextStyle, color_eyre::eyre::Error> {
|
.get_or_try_init(|| -> Result<SkTextStyle, color_eyre::eyre::Error> {
|
||||||
let font = self
|
let font = self
|
||||||
.font_path
|
.font_path
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.and_then(|path| sk.font_create(path).ok())
|
.and_then(|path| sk.font_create(path).ok())
|
||||||
.unwrap_or_else(|| sk.font_find("default/font").unwrap());
|
.unwrap_or_else(|| sk.font_find("default/font").unwrap());
|
||||||
Ok(unsafe { sk.text_make_style(font, 1.0, WHITE) })
|
Ok(unsafe { sk.text_make_style(font, 1.0, WHITE) })
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(style) = style {
|
if let Ok(style) = style {
|
||||||
|
let text = self.text.lock();
|
||||||
let data = self.data.lock();
|
let data = self.data.lock();
|
||||||
let transform = self.space.global_transform()
|
let transform = self.space.global_transform()
|
||||||
* Mat4::from_scale(vec3(
|
* Mat4::from_scale(vec3(
|
||||||
@@ -109,61 +81,55 @@ impl Text {
|
|||||||
data.character_height,
|
data.character_height,
|
||||||
data.character_height,
|
data.character_height,
|
||||||
));
|
));
|
||||||
if let Some(bounds) = data.bounds {
|
if let Some(bounds) = &data.bounds {
|
||||||
sk.text_add_in(
|
sk.text_add_in(
|
||||||
&data.text,
|
&*text,
|
||||||
transform,
|
transform,
|
||||||
bounds / data.character_height,
|
Vec2::from(bounds.bounds) / data.character_height,
|
||||||
data.fit,
|
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,
|
||||||
|
},
|
||||||
*style,
|
*style,
|
||||||
data.bounds_align,
|
convert_align(bounds.anchor_align_x.clone(), bounds.anchor_align_y.clone()),
|
||||||
data.text_align,
|
convert_align(data.text_align_x.clone(), data.text_align_y.clone()),
|
||||||
vec3(0.0, 0.0, 0.0),
|
vec3(0.0, 0.0, 0.0),
|
||||||
Color128::from([
|
Color128::from([data.color.c.r, data.color.c.g, data.color.c.b, data.color.a]),
|
||||||
data.color.red(),
|
|
||||||
data.color.green(),
|
|
||||||
data.color.blue(),
|
|
||||||
data.color.alpha(),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
sk.text_add_at(
|
sk.text_add_at(
|
||||||
&data.text,
|
&*text,
|
||||||
transform,
|
transform,
|
||||||
*style,
|
*style,
|
||||||
data.bounds_align,
|
TextAlign::Center,
|
||||||
data.text_align,
|
convert_align(data.text_align_x.clone(), data.text_align_y.clone()),
|
||||||
vec3(0.0, 0.0, 0.0),
|
vec3(0.0, 0.0, 0.0),
|
||||||
Color128::from([
|
Color128::from([data.color.c.r, data.color.c.g, data.color.c.b, data.color.a]),
|
||||||
data.color.red(),
|
|
||||||
data.color.green(),
|
|
||||||
data.color.blue(),
|
|
||||||
data.color.alpha(),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn set_character_height_flex(
|
impl Aspect for Text {
|
||||||
node: &Node,
|
const NAME: &'static str = "Text";
|
||||||
|
}
|
||||||
|
impl TextAspect for Text {
|
||||||
|
fn set_character_height(
|
||||||
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
height: f32,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(Drawable::Text(text)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let this_text = node.get_aspect::<Text>()?;
|
||||||
|
this_text.data.lock().character_height = height;
|
||||||
text.data.lock().character_height = deserialize(message.as_ref())?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text_flex(
|
fn set_text(node: Arc<Node>, _calling_client: Arc<Client>, text: String) -> Result<()> {
|
||||||
node: &Node,
|
let this_text = node.get_aspect::<Text>()?;
|
||||||
_calling_client: Arc<Client>,
|
*this_text.text.lock() = text;
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(Drawable::Text(text)) = node.drawable.get() else {bail!("Not a drawable??")};
|
|
||||||
|
|
||||||
text.data.lock().text = deserialize(message.as_ref())?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,40 +149,3 @@ pub fn draw_all(sk: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateTextInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
text: String,
|
|
||||||
font_resource: Option<ResourceID>,
|
|
||||||
character_height: f32,
|
|
||||||
text_align: TextAlign,
|
|
||||||
bounds: Option<Vector2<f32>>,
|
|
||||||
fit: TextFit,
|
|
||||||
bounds_align: TextAlign,
|
|
||||||
color: [f32; 4],
|
|
||||||
}
|
|
||||||
let info: CreateTextInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/drawable/text", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
|
||||||
let color = Rgba::from_slice(&info.color);
|
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
Text::add_to(
|
|
||||||
&node,
|
|
||||||
info.font_resource,
|
|
||||||
info.text,
|
|
||||||
info.character_height,
|
|
||||||
info.text_align,
|
|
||||||
info.bounds,
|
|
||||||
info.fit,
|
|
||||||
info.bounds_align,
|
|
||||||
color,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{BoxFieldAspect, FieldTrait, Node};
|
||||||
use crate::core::client::Client;
|
use crate::nodes::fields::FieldAspect;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::spatial::Spatial;
|
||||||
use crate::nodes::Message;
|
use crate::{core::client::Client, nodes::fields::Field};
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec3, vec3a, Vec3, Vec3A};
|
use glam::{vec3, vec3a, Vec3, Vec3A};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct BoxField {
|
pub struct BoxField {
|
||||||
@@ -17,40 +14,19 @@ pub struct BoxField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BoxField {
|
impl BoxField {
|
||||||
pub fn add_to(node: &Arc<Node>, size: Vector3<f32>) -> Result<Arc<Field>> {
|
pub fn add_to(node: &Arc<Node>, size: Vector3<f32>) {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
node.field.get().is_none(),
|
|
||||||
"Internal: Node already has a field attached!"
|
|
||||||
);
|
|
||||||
let box_field = BoxField {
|
let box_field = BoxField {
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
size: Mutex::new(size.into()),
|
size: Mutex::new(size.into()),
|
||||||
};
|
};
|
||||||
box_field.add_field_methods(node);
|
<BoxField as FieldAspect>::add_node_members(node);
|
||||||
node.add_local_signal("set_size", BoxField::set_size_flex);
|
<BoxField as BoxFieldAspect>::add_node_members(node);
|
||||||
let field = Arc::new(Field::Box(box_field));
|
node.add_aspect(Field::Box(box_field));
|
||||||
let _ = node.field.set(field.clone());
|
|
||||||
Ok(field)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size(&self, size: Vector3<f32>) {
|
pub fn set_size(&self, size: Vector3<f32>) {
|
||||||
*self.size.lock() = size.into();
|
*self.size.lock() = size.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Box(box_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
|
||||||
box_field.set_size(deserialize(message.as_ref())?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldTrait for BoxField {
|
impl FieldTrait for BoxField {
|
||||||
@@ -68,25 +44,17 @@ impl FieldTrait for BoxField {
|
|||||||
self.space.as_ref()
|
self.space.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl BoxFieldAspect for BoxField {
|
||||||
pub fn create_box_field_flex(
|
fn set_size(
|
||||||
_node: &Node,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
size: mint::Vector3<f32>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
struct CreateFieldInfo<'a> {
|
let Field::Box(this_field) = &*this_field else {
|
||||||
name: &'a str,
|
return Ok(());
|
||||||
parent_path: &'a str,
|
};
|
||||||
transform: Transform,
|
this_field.set_size(size.into());
|
||||||
size: Vector3<f32>,
|
Ok(())
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
BoxField::add_to(&node, info.size)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{CylinderFieldAspect, Field, FieldTrait, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::fields::FieldAspect;
|
||||||
use crate::nodes::Message;
|
use crate::nodes::spatial::Spatial;
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::Result;
|
||||||
use glam::{swizzles::*, vec2, Vec3A};
|
use glam::{swizzles::*, vec2, Vec3A};
|
||||||
use portable_atomic::AtomicF32;
|
use portable_atomic::AtomicF32;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -19,43 +16,22 @@ pub struct CylinderField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CylinderField {
|
impl CylinderField {
|
||||||
pub fn add_to(node: &Arc<Node>, length: f32, radius: f32) -> Result<()> {
|
pub fn add_to(node: &Arc<Node>, length: f32, radius: f32) {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
node.field.get().is_none(),
|
|
||||||
"Internal: Node already has a field attached!"
|
|
||||||
);
|
|
||||||
let cylinder_field = CylinderField {
|
let cylinder_field = CylinderField {
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
length: AtomicF32::new(length.abs()),
|
length: AtomicF32::new(length.abs()),
|
||||||
radius: AtomicF32::new(radius.abs()),
|
radius: AtomicF32::new(radius.abs()),
|
||||||
};
|
};
|
||||||
cylinder_field.add_field_methods(node);
|
<CylinderField as FieldAspect>::add_node_members(node);
|
||||||
node.add_local_signal("set_size", CylinderField::set_size_flex);
|
<CylinderField as CylinderFieldAspect>::add_node_members(node);
|
||||||
let _ = node.field.set(Arc::new(Field::Cylinder(cylinder_field)));
|
node.add_aspect(Field::Cylinder(cylinder_field));
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size(&self, length: f32, radius: f32) {
|
pub fn set_size(&self, length: f32, radius: f32) {
|
||||||
self.length.store(length.abs(), Ordering::Relaxed);
|
self.length.store(length.abs(), Ordering::Relaxed);
|
||||||
self.radius.store(radius.abs(), Ordering::Relaxed);
|
self.radius.store(radius.abs(), Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Cylinder(cylinder_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
|
||||||
let (length, radius) = deserialize(message.as_ref())?;
|
|
||||||
cylinder_field.set_size(length, radius);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldTrait for CylinderField {
|
impl FieldTrait for CylinderField {
|
||||||
fn local_distance(&self, p: Vec3A) -> f32 {
|
fn local_distance(&self, p: Vec3A) -> f32 {
|
||||||
let radius = self.radius.load(Ordering::Relaxed);
|
let radius = self.radius.load(Ordering::Relaxed);
|
||||||
@@ -68,26 +44,18 @@ impl FieldTrait for CylinderField {
|
|||||||
self.space.as_ref()
|
self.space.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CylinderFieldAspect for CylinderField {
|
||||||
pub fn create_cylinder_field_flex(
|
fn set_size(
|
||||||
_node: &Node,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateFieldInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
length: f32,
|
length: f32,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
|
let Field::Cylinder(this_field) = &*this_field else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
this_field.set_size(length, radius);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
CylinderField::add_to(&node, info.length, info.radius)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,39 +3,34 @@ mod cylinder;
|
|||||||
mod sphere;
|
mod sphere;
|
||||||
mod torus;
|
mod torus;
|
||||||
|
|
||||||
use self::cylinder::{create_cylinder_field_flex, CylinderField};
|
use self::cylinder::CylinderField;
|
||||||
use self::r#box::{create_box_field_flex, BoxField};
|
use self::r#box::BoxField;
|
||||||
use self::sphere::{create_sphere_field_flex, SphereField};
|
use self::sphere::SphereField;
|
||||||
use self::torus::{create_torus_field_flex, TorusField};
|
use self::torus::TorusField;
|
||||||
|
|
||||||
use super::alias::AliasInfo;
|
use super::alias::AliasInfo;
|
||||||
use super::spatial::Spatial;
|
use super::spatial::Spatial;
|
||||||
use super::{Message, Node};
|
use super::{Aspect, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
use crate::create_interface;
|
||||||
use crate::nodes::spatial::find_reference_space;
|
use crate::nodes::spatial::Transform;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec2, vec3a, Vec3, Vec3A};
|
use glam::{vec2, vec3a, Mat4, Vec3, Vec3A};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
// 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: Lazy<AliasInfo> = Lazy::new(|| AliasInfo {
|
||||||
server_methods: vec!["distance", "normal", "closest_point", "ray_march"],
|
server_methods: vec!["distance", "normal", "closest_point", "ray_march"],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
pub trait FieldTrait {
|
stardust_xr_server_codegen::codegen_field_protocol!();
|
||||||
fn add_field_methods(&self, node: &Arc<Node>) {
|
|
||||||
node.add_local_method("distance", field_distance_flex);
|
pub trait FieldTrait: Send + Sync + 'static {
|
||||||
node.add_local_method("normal", field_normal_flex);
|
|
||||||
node.add_local_method("closest_point", field_closest_point_flex);
|
|
||||||
node.add_local_method("ray_march", field_ray_march_flex);
|
|
||||||
}
|
|
||||||
fn spatial_ref(&self) -> &Spatial;
|
fn spatial_ref(&self) -> &Spatial;
|
||||||
|
|
||||||
fn local_distance(&self, p: Vec3A) -> f32;
|
fn local_distance(&self, p: Vec3A) -> f32;
|
||||||
@@ -81,6 +76,8 @@ pub trait FieldTrait {
|
|||||||
|
|
||||||
fn ray_march(&self, ray: Ray) -> RayMarchResult {
|
fn ray_march(&self, ray: Ray) -> RayMarchResult {
|
||||||
let mut result = RayMarchResult {
|
let mut result = RayMarchResult {
|
||||||
|
ray_origin: ray.origin.into(),
|
||||||
|
ray_direction: ray.direction.into(),
|
||||||
min_distance: f32::MAX,
|
min_distance: f32::MAX,
|
||||||
deepest_point_distance: 0_f32,
|
deepest_point_distance: 0_f32,
|
||||||
ray_length: 0_f32,
|
ray_length: 0_f32,
|
||||||
@@ -90,7 +87,9 @@ pub trait FieldTrait {
|
|||||||
let ray_to_field_matrix =
|
let ray_to_field_matrix =
|
||||||
Spatial::space_to_space_matrix(Some(&ray.space), Some(self.spatial_ref()));
|
Spatial::space_to_space_matrix(Some(&ray.space), Some(self.spatial_ref()));
|
||||||
let mut ray_point = ray_to_field_matrix.transform_point3a(ray.origin.into());
|
let mut ray_point = ray_to_field_matrix.transform_point3a(ray.origin.into());
|
||||||
let ray_direction = ray_to_field_matrix.transform_vector3a(ray.direction.into());
|
let ray_direction = ray_to_field_matrix
|
||||||
|
.transform_vector3a(ray.direction.into())
|
||||||
|
.normalize();
|
||||||
|
|
||||||
while result.ray_steps < MAX_RAY_STEPS && result.ray_length < MAX_RAY_LENGTH {
|
while result.ray_steps < MAX_RAY_STEPS && result.ray_length < MAX_RAY_LENGTH {
|
||||||
let distance = self.local_distance(ray_point);
|
let distance = self.local_distance(ray_point);
|
||||||
@@ -110,6 +109,60 @@ pub trait FieldTrait {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<Fi: FieldTrait + 'static> FieldAspect for Fi {
|
||||||
|
async fn distance(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
space: Arc<Node>,
|
||||||
|
point: mint::Vector3<f32>,
|
||||||
|
) -> Result<f32> {
|
||||||
|
let reference_space = space.get_aspect::<Spatial>()?;
|
||||||
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
|
Ok((*this_field).distance(reference_space.as_ref(), point.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn normal(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
space: Arc<Node>,
|
||||||
|
point: mint::Vector3<f32>,
|
||||||
|
) -> Result<Vector3<f32>> {
|
||||||
|
let reference_space = space.get_aspect::<Spatial>()?;
|
||||||
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
|
Ok(this_field
|
||||||
|
.normal(reference_space.as_ref(), point.into(), 0.001)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn closest_point(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
space: Arc<Node>,
|
||||||
|
point: mint::Vector3<f32>,
|
||||||
|
) -> Result<Vector3<f32>> {
|
||||||
|
let reference_space = space.get_aspect::<Spatial>()?;
|
||||||
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
|
Ok(this_field
|
||||||
|
.closest_point(reference_space.as_ref(), point.into(), 0.001)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ray_march(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
space: Arc<Node>,
|
||||||
|
ray_origin: mint::Vector3<f32>,
|
||||||
|
ray_direction: mint::Vector3<f32>,
|
||||||
|
) -> Result<RayMarchResult> {
|
||||||
|
let reference_space = space.get_aspect::<Spatial>()?;
|
||||||
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
|
Ok(this_field.ray_march(Ray {
|
||||||
|
origin: ray_origin.into(),
|
||||||
|
direction: ray_direction.into(),
|
||||||
|
space: reference_space.clone(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
pub origin: Vec3,
|
pub origin: Vec3,
|
||||||
@@ -117,14 +170,6 @@ pub struct Ray {
|
|||||||
pub space: Arc<Spatial>,
|
pub space: Arc<Spatial>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct RayMarchResult {
|
|
||||||
pub min_distance: f32,
|
|
||||||
pub deepest_point_distance: f32,
|
|
||||||
pub ray_length: f32,
|
|
||||||
pub ray_steps: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// const MIN_RAY_STEPS: u32 = 0;
|
// const MIN_RAY_STEPS: u32 = 0;
|
||||||
const MAX_RAY_STEPS: u32 = 1000;
|
const MAX_RAY_STEPS: u32 = 1000;
|
||||||
|
|
||||||
@@ -134,107 +179,15 @@ const MAX_RAY_MARCH: f32 = f32::MAX;
|
|||||||
// const MIN_RAY_LENGTH: f32 = 0_f32;
|
// const MIN_RAY_LENGTH: f32 = 0_f32;
|
||||||
const MAX_RAY_LENGTH: f32 = 1000_f32;
|
const MAX_RAY_LENGTH: f32 = 1000_f32;
|
||||||
|
|
||||||
fn field_distance_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct FieldInfoArgs<'a> {
|
|
||||||
reference_space_path: &'a str,
|
|
||||||
point: Vector3<f32>,
|
|
||||||
}
|
|
||||||
let args: FieldInfoArgs = deserialize(message.as_ref())?;
|
|
||||||
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
|
||||||
|
|
||||||
let distance = node
|
|
||||||
.field
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.distance(reference_space.as_ref(), args.point.into());
|
|
||||||
Ok(serialize(distance)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fn field_normal_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct FieldInfoArgs<'a> {
|
|
||||||
reference_space_path: &'a str,
|
|
||||||
point: Vector3<f32>,
|
|
||||||
}
|
|
||||||
let args: FieldInfoArgs = deserialize(message.as_ref())?;
|
|
||||||
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
|
||||||
|
|
||||||
let normal = node.field.get().as_ref().unwrap().normal(
|
|
||||||
reference_space.as_ref(),
|
|
||||||
args.point.into(),
|
|
||||||
0.001,
|
|
||||||
);
|
|
||||||
Ok(serialize(mint::Vector3::from(normal))?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fn field_closest_point_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct FieldInfoArgs<'a> {
|
|
||||||
reference_space_path: &'a str,
|
|
||||||
point: Vector3<f32>,
|
|
||||||
}
|
|
||||||
let args: FieldInfoArgs = deserialize(message.as_ref())?;
|
|
||||||
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
|
||||||
|
|
||||||
let closest_point = node.field.get().as_ref().unwrap().closest_point(
|
|
||||||
reference_space.as_ref(),
|
|
||||||
args.point.into(),
|
|
||||||
0.001,
|
|
||||||
);
|
|
||||||
Ok(serialize(mint::Vector3::from(closest_point))?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fn field_ray_march_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct FieldInfoArgs<'a> {
|
|
||||||
reference_space_path: &'a str,
|
|
||||||
ray_origin: Vector3<f32>,
|
|
||||||
ray_direction: Vector3<f32>,
|
|
||||||
}
|
|
||||||
let args: FieldInfoArgs = deserialize(message.as_ref())?;
|
|
||||||
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
|
||||||
|
|
||||||
let ray_march_result = node.field.get().unwrap().ray_march(Ray {
|
|
||||||
origin: args.ray_origin.into(),
|
|
||||||
direction: args.ray_direction.into(),
|
|
||||||
space: reference_space,
|
|
||||||
});
|
|
||||||
Ok(serialize(ray_march_result)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Field {
|
pub enum Field {
|
||||||
Box(BoxField),
|
Box(BoxField),
|
||||||
Cylinder(CylinderField),
|
Cylinder(CylinderField),
|
||||||
Sphere(SphereField),
|
Sphere(SphereField),
|
||||||
Torus(TorusField),
|
Torus(TorusField),
|
||||||
}
|
}
|
||||||
|
impl Aspect for Field {
|
||||||
|
const NAME: &'static str = "Field";
|
||||||
|
}
|
||||||
impl Deref for Field {
|
impl Deref for Field {
|
||||||
type Target = dyn FieldTrait;
|
type Target = dyn FieldTrait;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -246,19 +199,171 @@ impl Deref for Field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// impl FieldTrait for Field {
|
||||||
|
// fn spatial_ref(&self) -> &Spatial {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.spatial_ref(),
|
||||||
|
// Field::Cylinder(field) => field.spatial_ref(),
|
||||||
|
// Field::Sphere(field) => field.spatial_ref(),
|
||||||
|
// Field::Torus(field) => field.spatial_ref(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn local_distance(&self, p: Vec3A) -> f32 {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.local_distance(p),
|
||||||
|
// Field::Cylinder(field) => field.local_distance(p),
|
||||||
|
// Field::Sphere(field) => field.local_distance(p),
|
||||||
|
// Field::Torus(field) => field.local_distance(p),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn local_normal(&self, p: Vec3A, r: f32) -> Vec3A {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.local_normal(p, r),
|
||||||
|
// Field::Cylinder(field) => field.local_normal(p, r),
|
||||||
|
// Field::Sphere(field) => field.local_normal(p, r),
|
||||||
|
// Field::Torus(field) => field.local_normal(p, r),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn local_closest_point(&self, p: Vec3A, r: f32) -> Vec3A {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.local_closest_point(p, r),
|
||||||
|
// Field::Cylinder(field) => field.local_closest_point(p, r),
|
||||||
|
// Field::Sphere(field) => field.local_closest_point(p, r),
|
||||||
|
// Field::Torus(field) => field.local_closest_point(p, r),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn distance(&self, reference_space: &Spatial, p: Vec3A) -> f32 {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.distance(reference_space, p),
|
||||||
|
// Field::Cylinder(field) => field.distance(reference_space, p),
|
||||||
|
// Field::Sphere(field) => field.distance(reference_space, p),
|
||||||
|
// Field::Torus(field) => field.distance(reference_space, p),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn normal(&self, reference_space: &Spatial, p: Vec3A, r: f32) -> Vec3A {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.normal(reference_space, p, r),
|
||||||
|
// Field::Cylinder(field) => field.normal(reference_space, p, r),
|
||||||
|
// Field::Sphere(field) => field.normal(reference_space, p, r),
|
||||||
|
// Field::Torus(field) => field.normal(reference_space, p, r),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn closest_point(&self, reference_space: &Spatial, p: Vec3A, r: f32) -> Vec3A {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.closest_point(reference_space, p, r),
|
||||||
|
// Field::Cylinder(field) => field.closest_point(reference_space, p, r),
|
||||||
|
// Field::Sphere(field) => field.closest_point(reference_space, p, r),
|
||||||
|
// Field::Torus(field) => field.closest_point(reference_space, p, r),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fn ray_march(&self, ray: Ray) -> RayMarchResult {
|
||||||
|
// match self {
|
||||||
|
// Field::Box(field) => field.ray_march(ray),
|
||||||
|
// Field::Cylinder(field) => field.ray_march(ray),
|
||||||
|
// Field::Sphere(field) => field.ray_march(ray),
|
||||||
|
// Field::Torus(field) => field.ray_march(ray),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
create_interface!(FieldInterface, FieldInterfaceAspect, "/field");
|
||||||
let node = Node::create(client, "", "field", false);
|
pub struct FieldInterface;
|
||||||
node.add_local_signal("create_box_field", create_box_field_flex);
|
impl FieldInterfaceAspect for FieldInterface {
|
||||||
node.add_local_signal("create_cylinder_field", create_cylinder_field_flex);
|
fn create_box_field(
|
||||||
node.add_local_signal("create_sphere_field", create_sphere_field_flex);
|
_node: Arc<Node>,
|
||||||
node.add_local_signal("create_torus_field", create_torus_field_flex);
|
calling_client: Arc<Client>,
|
||||||
node.add_to_scenegraph().map(|_| ())
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
size: mint::Vector3<f32>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let transform = transform.to_mat4(true, true, false);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let node = Node::create_parent_name(
|
||||||
|
&calling_client,
|
||||||
|
Self::CREATE_BOX_FIELD_PARENT_PATH,
|
||||||
|
&name,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
BoxField::add_to(&node, size);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_cylinder_field(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
length: f32,
|
||||||
|
radius: f32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let transform = transform.to_mat4(true, true, false);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let node = Node::create_parent_name(
|
||||||
|
&calling_client,
|
||||||
|
Self::CREATE_CYLINDER_FIELD_PARENT_PATH,
|
||||||
|
&name,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
CylinderField::add_to(&node, length, radius);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_sphere_field(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
position: mint::Vector3<f32>,
|
||||||
|
radius: f32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let node = Node::create_parent_name(
|
||||||
|
&calling_client,
|
||||||
|
Self::CREATE_SPHERE_FIELD_PARENT_PATH,
|
||||||
|
&name,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(
|
||||||
|
&node,
|
||||||
|
Some(parent.clone()),
|
||||||
|
Mat4::from_translation(position.into()),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
SphereField::add_to(&node, radius);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_torus_field(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
radius_a: f32,
|
||||||
|
radius_b: f32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let transform = transform.to_mat4(true, true, false);
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let node = Node::create_parent_name(
|
||||||
|
&calling_client,
|
||||||
|
Self::CREATE_TORUS_FIELD_PARENT_PATH,
|
||||||
|
&name,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
TorusField::add_to(&node, radius_a, radius_b);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_field(client: &Client, path: &str) -> Result<Arc<Field>> {
|
pub fn find_field(client: &Client, path: &str) -> Result<Arc<Field>> {
|
||||||
client
|
client.get_node("Field", path)?.get_aspect::<Field>()
|
||||||
.get_node("Field", path)?
|
|
||||||
.get_aspect("Field", "info", |n| &n.field)
|
|
||||||
.cloned()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{Field, FieldTrait, Node, SphereFieldAspect};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, Spatial};
|
use crate::nodes::fields::FieldAspect;
|
||||||
use crate::nodes::Message;
|
use crate::nodes::spatial::Spatial;
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::Result;
|
||||||
use glam::{Mat4, Vec3A};
|
use glam::Vec3A;
|
||||||
use mint::Vector3;
|
|
||||||
use portable_atomic::AtomicF32;
|
use portable_atomic::AtomicF32;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -17,38 +14,19 @@ pub struct SphereField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SphereField {
|
impl SphereField {
|
||||||
pub fn add_to(node: &Arc<Node>, radius: f32) -> Result<()> {
|
pub fn add_to(node: &Arc<Node>, radius: f32) {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
node.field.get().is_none(),
|
|
||||||
"Internal: Node already has a field attached!"
|
|
||||||
);
|
|
||||||
let sphere_field = SphereField {
|
let sphere_field = SphereField {
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
radius: AtomicF32::new(radius),
|
radius: AtomicF32::new(radius),
|
||||||
};
|
};
|
||||||
sphere_field.add_field_methods(node);
|
<SphereField as FieldAspect>::add_node_members(node);
|
||||||
node.add_local_signal("set_radius", SphereField::set_radius_flex);
|
<SphereField as SphereFieldAspect>::add_node_members(node);
|
||||||
let _ = node.field.set(Arc::new(Field::Sphere(sphere_field)));
|
node.add_aspect(Field::Sphere(sphere_field));
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_radius(&self, radius: f32) {
|
pub fn set_radius(&self, radius: f32) {
|
||||||
self.radius.store(radius, Ordering::Relaxed);
|
self.radius.store(radius, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_radius_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Sphere(sphere_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
|
||||||
sphere_field.set_radius(deserialize(message.as_ref())?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldTrait for SphereField {
|
impl FieldTrait for SphereField {
|
||||||
@@ -65,29 +43,13 @@ impl FieldTrait for SphereField {
|
|||||||
self.space.as_ref()
|
self.space.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl SphereFieldAspect for SphereField {
|
||||||
pub fn create_sphere_field_flex(
|
fn set_radius(node: Arc<Node>, _calling_client: Arc<Client>, radius: f32) -> Result<()> {
|
||||||
_node: &Node,
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
calling_client: Arc<Client>,
|
let Field::Sphere(this_field) = &*this_field else {
|
||||||
message: Message,
|
return Ok(());
|
||||||
) -> Result<()> {
|
};
|
||||||
#[derive(Deserialize)]
|
this_field.set_radius(radius);
|
||||||
struct CreateFieldInfo<'a> {
|
Ok(())
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
origin: Option<Vector3<f32>>,
|
|
||||||
radius: f32,
|
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = Mat4::from_translation(
|
|
||||||
info.origin
|
|
||||||
.unwrap_or_else(|| Vector3::from([0.0; 3]))
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
SphereField::add_to(&node, info.radius)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{Field, FieldTrait, Node, TorusFieldAspect};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::fields::FieldAspect;
|
||||||
use crate::nodes::Message;
|
use crate::nodes::spatial::Spatial;
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::Result;
|
||||||
use glam::{swizzles::*, vec2, Vec3A};
|
use glam::{swizzles::*, vec2, Vec3A};
|
||||||
use portable_atomic::AtomicF32;
|
use portable_atomic::AtomicF32;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -19,44 +16,22 @@ pub struct TorusField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TorusField {
|
impl TorusField {
|
||||||
pub fn add_to(node: &Arc<Node>, radius_a: f32, radius_b: f32) -> Result<()> {
|
pub fn add_to(node: &Arc<Node>, radius_a: f32, radius_b: f32) {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
node.field.get().is_none(),
|
|
||||||
"Internal: Node already has a field attached!"
|
|
||||||
);
|
|
||||||
let torus_field = TorusField {
|
let torus_field = TorusField {
|
||||||
space: node.spatial.get().unwrap().clone(),
|
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
radius_a: AtomicF32::new(radius_a.abs()),
|
radius_a: AtomicF32::new(radius_a.abs()),
|
||||||
radius_b: AtomicF32::new(radius_b.abs()),
|
radius_b: AtomicF32::new(radius_b.abs()),
|
||||||
};
|
};
|
||||||
torus_field.add_field_methods(node);
|
<TorusField as FieldAspect>::add_node_members(node);
|
||||||
node.add_local_signal("set_size", TorusField::set_size_flex);
|
<TorusField as TorusFieldAspect>::add_node_members(node);
|
||||||
let _ = node.field.set(Arc::new(Field::Torus(torus_field)));
|
node.add_aspect(Field::Torus(torus_field));
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size(&self, radius_a: f32, radius_b: f32) {
|
pub fn set_size(&self, radius_a: f32, radius_b: f32) {
|
||||||
self.radius_a.store(radius_a.abs(), Ordering::Relaxed);
|
self.radius_a.store(radius_a.abs(), Ordering::Relaxed);
|
||||||
self.radius_b.store(radius_b.abs(), Ordering::Relaxed);
|
self.radius_b.store(radius_b.abs(), Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Torus(torus_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
|
||||||
let (radius_a, radius_b) = deserialize(message.as_ref())?;
|
|
||||||
torus_field.set_size(radius_a, radius_b);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldTrait for TorusField {
|
impl FieldTrait for TorusField {
|
||||||
fn local_distance(&self, p: Vec3A) -> f32 {
|
fn local_distance(&self, p: Vec3A) -> f32 {
|
||||||
let radius_a = self.radius_a.load(Ordering::Relaxed);
|
let radius_a = self.radius_a.load(Ordering::Relaxed);
|
||||||
@@ -68,26 +43,18 @@ impl FieldTrait for TorusField {
|
|||||||
self.space.as_ref()
|
self.space.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl TorusFieldAspect for TorusField {
|
||||||
pub fn create_torus_field_flex(
|
fn set_size(
|
||||||
_node: &Node,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateFieldInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
radius_a: f32,
|
radius_a: f32,
|
||||||
radius_b: f32,
|
radius_b: f32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_field = node.get_aspect::<Field>()?;
|
||||||
|
let Field::Torus(this_field) = &*this_field else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
this_field.set_size(radius_a, radius_b);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
TorusField::add_to(&node, info.radius_a, info.radius_b)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,14 @@ lazy_static::lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create() -> Arc<Node> {
|
fn create() -> Arc<Node> {
|
||||||
let node = Arc::new(Node::create(&INTERNAL_CLIENT, "", "hmd", false));
|
let node = Arc::new(Node::create_parent_name(&INTERNAL_CLIENT, "", "hmd", false));
|
||||||
Spatial::add_to(&node, None, Mat4::IDENTITY, false).expect("Unable to make spatial for HMD");
|
Spatial::add_to(&node, None, Mat4::IDENTITY, false);
|
||||||
|
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame(sk: &impl StereoKitMultiThread) {
|
pub fn frame(sk: &impl StereoKitMultiThread) {
|
||||||
let spatial = HMD
|
let spatial = HMD.get_aspect::<Spatial>().unwrap();
|
||||||
.spatial
|
|
||||||
.get()
|
|
||||||
.expect("Unable to get spatial to update HMD");
|
|
||||||
let hmd_pose = sk.input_head();
|
let hmd_pose = sk.input_head();
|
||||||
*spatial.transform.lock() = Mat4::from_scale_rotation_translation(
|
*spatial.transform.lock() = Mat4::from_scale_rotation_translation(
|
||||||
vec3(1.0, 1.0, 1.0),
|
vec3(1.0, 1.0, 1.0),
|
||||||
|
|||||||
@@ -9,23 +9,22 @@ use self::tip::Tip;
|
|||||||
use super::{
|
use super::{
|
||||||
alias::{Alias, AliasInfo},
|
alias::{Alias, AliasInfo},
|
||||||
fields::{find_field, Field, FIELD_ALIAS_INFO},
|
fields::{find_field, Field, FIELD_ALIAS_INFO},
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
spatial::{parse_transform, Spatial},
|
||||||
Message, Node,
|
Aspect, Message, Node,
|
||||||
};
|
};
|
||||||
use crate::core::registry::Registry;
|
|
||||||
use crate::core::{client::Client, node_collections::LifeLinkedNodeMap};
|
use crate::core::{client::Client, node_collections::LifeLinkedNodeMap};
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use crate::{core::registry::Registry, nodes::spatial::Transform};
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::AtomicBool;
|
use portable_atomic::AtomicBool;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::schemas::{flat::InputData, flex::deserialize};
|
use stardust_xr::schemas::{flat::InputDataType, flex::serialize};
|
||||||
use stardust_xr::schemas::{
|
use stardust_xr::{
|
||||||
flat::{Datamap, InputDataType},
|
schemas::{flat::InputData, flex::deserialize},
|
||||||
flex::serialize,
|
values::Datamap,
|
||||||
};
|
};
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
@@ -77,11 +76,6 @@ impl InputMethod {
|
|||||||
specialization: InputType,
|
specialization: InputType,
|
||||||
datamap: Option<Datamap>,
|
datamap: Option<Datamap>,
|
||||||
) -> Result<Arc<InputMethod>> {
|
) -> Result<Arc<InputMethod>> {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
|
|
||||||
node.add_local_signal("capture", InputMethod::capture_flex);
|
node.add_local_signal("capture", InputMethod::capture_flex);
|
||||||
node.add_local_signal("set_datamap", InputMethod::set_datamap_flex);
|
node.add_local_signal("set_datamap", InputMethod::set_datamap_flex);
|
||||||
node.add_local_signal("set_handlers", InputMethod::set_handlers_flex);
|
node.add_local_signal("set_handlers", InputMethod::set_handlers_flex);
|
||||||
@@ -90,7 +84,7 @@ impl InputMethod {
|
|||||||
node: Arc::downgrade(node),
|
node: Arc::downgrade(node),
|
||||||
uid: node.uid.clone(),
|
uid: node.uid.clone(),
|
||||||
enabled: Mutex::new(true),
|
enabled: Mutex::new(true),
|
||||||
spatial: node.spatial.get().unwrap().clone(),
|
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
specialization: Mutex::new(specialization),
|
specialization: Mutex::new(specialization),
|
||||||
captures: Registry::new(),
|
captures: Registry::new(),
|
||||||
datamap: Mutex::new(datamap),
|
datamap: Mutex::new(datamap),
|
||||||
@@ -102,28 +96,38 @@ impl InputMethod {
|
|||||||
method.make_alias(&handler);
|
method.make_alias(&handler);
|
||||||
}
|
}
|
||||||
let method = INPUT_METHOD_REGISTRY.add(method);
|
let method = INPUT_METHOD_REGISTRY.add(method);
|
||||||
let _ = node.input_method.set(method.clone());
|
node.add_aspect_raw(method.clone());
|
||||||
Ok(method)
|
Ok(method)
|
||||||
}
|
}
|
||||||
fn get(node: &Node) -> Result<Arc<Self>> {
|
fn get(node: &Node) -> Result<Arc<Self>> {
|
||||||
node.get_aspect("Input Method", "input method", |n| &n.input_method)
|
node.get_aspect::<Self>()
|
||||||
.cloned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capture_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn capture_flex(node: Arc<Node>, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
||||||
let method = InputMethod::get(node)?;
|
let method = InputMethod::get(&node)?;
|
||||||
let handler = InputHandler::find(&calling_client, deserialize(message.as_ref())?)?;
|
let handler = InputHandler::find(&calling_client, deserialize(message.as_ref())?)?;
|
||||||
|
|
||||||
method.captures.add_raw(&handler);
|
method.captures.add_raw(&handler);
|
||||||
node.send_remote_signal("capture", message)
|
node.send_remote_signal("capture", message)
|
||||||
}
|
}
|
||||||
fn set_datamap_flex(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn set_datamap_flex(
|
||||||
let method = InputMethod::get(node)?;
|
node: Arc<Node>,
|
||||||
method.datamap.lock().replace(Datamap::new(message.data)?);
|
_calling_client: Arc<Client>,
|
||||||
|
message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
|
let method = InputMethod::get(&node)?;
|
||||||
|
method
|
||||||
|
.datamap
|
||||||
|
.lock()
|
||||||
|
.replace(Datamap::from_raw(message.data)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn set_handlers_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn set_handlers_flex(
|
||||||
let method = InputMethod::get(node)?;
|
node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
|
let method = InputMethod::get(&node)?;
|
||||||
let handler_paths: Vec<&str> = deserialize(message.as_ref())?;
|
let handler_paths: Vec<&str> = deserialize(message.as_ref())?;
|
||||||
let handlers: Vec<Weak<InputHandler>> = handler_paths
|
let handlers: Vec<Weak<InputHandler>> = handler_paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -139,9 +143,15 @@ impl InputMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_alias(&self, handler: &InputHandler) {
|
fn make_alias(&self, handler: &InputHandler) {
|
||||||
let Some(method_node) = self.node.upgrade() else {return};
|
let Some(method_node) = self.node.upgrade() else {
|
||||||
let Some(handler_node) = handler.node.upgrade() else {return};
|
return;
|
||||||
let Some(client) = handler_node.get_client() else {return};
|
};
|
||||||
|
let Some(handler_node) = handler.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(client) = handler_node.get_client() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let Ok(method_alias) = Alias::create(
|
let Ok(method_alias) = Alias::create(
|
||||||
&client,
|
&client,
|
||||||
handler_node.get_path(),
|
handler_node.get_path(),
|
||||||
@@ -151,7 +161,9 @@ impl InputMethod {
|
|||||||
server_signals: vec!["capture"],
|
server_signals: vec!["capture"],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
) else {return};
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
method_alias.enabled.store(false, Ordering::Relaxed);
|
method_alias.enabled.store(false, Ordering::Relaxed);
|
||||||
handler
|
handler
|
||||||
.method_aliases
|
.method_aliases
|
||||||
@@ -168,9 +180,15 @@ impl InputMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_new_handler(&self, handler: &InputHandler) {
|
fn handle_new_handler(&self, handler: &InputHandler) {
|
||||||
let Some(method_node) = self.node.upgrade() else {return};
|
let Some(method_node) = self.node.upgrade() else {
|
||||||
let Some(method_client) = method_node.get_client() else {return};
|
return;
|
||||||
let Some(handler_node) = handler.node.upgrade() else {return};
|
};
|
||||||
|
let Some(method_client) = method_node.get_client() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(handler_node) = handler.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
// Receiver itself
|
// Receiver itself
|
||||||
let Ok(handler_alias) = Alias::create(
|
let Ok(handler_alias) = Alias::create(
|
||||||
&method_client,
|
&method_client,
|
||||||
@@ -178,38 +196,49 @@ impl InputMethod {
|
|||||||
handler.uid.as_str(),
|
handler.uid.as_str(),
|
||||||
&handler_node,
|
&handler_node,
|
||||||
AliasInfo {
|
AliasInfo {
|
||||||
server_methods: vec!["getTransform"],
|
server_methods: vec!["get_transform"],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
) else {return};
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.handler_aliases
|
self.handler_aliases
|
||||||
.add(handler.uid.clone(), &handler_alias);
|
.add(handler.uid.clone(), &handler_alias);
|
||||||
|
|
||||||
if let Some(handler_field_node) = handler.field.spatial_ref().node.upgrade() {
|
if let Some(handler_field_node) = handler.field.spatial_ref().node.upgrade() {
|
||||||
// Handler's field
|
// Handler's field
|
||||||
let Ok(rx_field_alias) = Alias::create(
|
let Ok(rx_field_alias) = Alias::create(
|
||||||
&method_client,
|
&method_client,
|
||||||
handler_alias.get_path(),
|
handler_alias.get_path(),
|
||||||
"field",
|
"field",
|
||||||
&handler_field_node,
|
&handler_field_node,
|
||||||
FIELD_ALIAS_INFO.clone(),
|
FIELD_ALIAS_INFO.clone(),
|
||||||
) else {return};
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.handler_aliases
|
self.handler_aliases
|
||||||
.add(handler.uid.clone() + "-field", &rx_field_alias);
|
.add(handler.uid.clone() + "-field", &rx_field_alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(data) = serialize(&handler.uid) else {return};
|
let Ok(data) = serialize(&handler.uid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = method_node.send_remote_signal("handler_created", data);
|
let _ = method_node.send_remote_signal("handler_created", data);
|
||||||
}
|
}
|
||||||
fn handle_drop_handler(&self, handler: &InputHandler) {
|
fn handle_drop_handler(&self, handler: &InputHandler) {
|
||||||
let uid = handler.uid.as_str();
|
let uid = handler.uid.as_str();
|
||||||
self.handler_aliases.remove(uid);
|
self.handler_aliases.remove(uid);
|
||||||
self.handler_aliases.remove(&(uid.to_string() + "-field"));
|
self.handler_aliases.remove(&(uid.to_string() + "-field"));
|
||||||
let Some(tx_node) = self.node.upgrade() else {return};
|
let Some(tx_node) = self.node.upgrade() else {
|
||||||
let Ok(data) = serialize(&uid) else {return};
|
return;
|
||||||
|
};
|
||||||
|
let Ok(data) = serialize(&uid) else { return };
|
||||||
let _ = tx_node.send_remote_signal("handler_destroyed", data);
|
let _ = tx_node.send_remote_signal("handler_destroyed", data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for InputMethod {
|
||||||
|
const NAME: &'static str = "InputMethod";
|
||||||
|
}
|
||||||
impl Drop for InputMethod {
|
impl Drop for InputMethod {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
INPUT_METHOD_REGISTRY.remove(self);
|
INPUT_METHOD_REGISTRY.remove(self);
|
||||||
@@ -230,11 +259,11 @@ impl DistanceLink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_input(&self, order: u32, datamap: Datamap) {
|
fn send_input(&self, order: u32, captured: bool, datamap: Datamap) {
|
||||||
self.handler.send_input(order, self, datamap);
|
self.handler.send_input(order, captured, self, datamap);
|
||||||
}
|
}
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn serialize(&self, order: u32, datamap: Datamap) -> Vec<u8> {
|
fn serialize(&self, order: u32, captured: bool, datamap: Datamap) -> Vec<u8> {
|
||||||
let input = self.method.specialization.lock().serialize(
|
let input = self.method.specialization.lock().serialize(
|
||||||
self,
|
self,
|
||||||
Spatial::space_to_space_matrix(Some(&self.method.spatial), Some(&self.handler.spatial)),
|
Spatial::space_to_space_matrix(Some(&self.method.spatial), Some(&self.handler.spatial)),
|
||||||
@@ -246,6 +275,7 @@ impl DistanceLink {
|
|||||||
distance: self.method.true_distance(&self.handler.field),
|
distance: self.method.true_distance(&self.handler.field),
|
||||||
datamap,
|
datamap,
|
||||||
order,
|
order,
|
||||||
|
captured,
|
||||||
};
|
};
|
||||||
root.serialize()
|
root.serialize()
|
||||||
}
|
}
|
||||||
@@ -261,16 +291,11 @@ pub struct InputHandler {
|
|||||||
}
|
}
|
||||||
impl InputHandler {
|
impl InputHandler {
|
||||||
pub fn add_to(node: &Arc<Node>, field: &Arc<Field>) -> Result<()> {
|
pub fn add_to(node: &Arc<Node>, field: &Arc<Field>) -> Result<()> {
|
||||||
ensure!(
|
|
||||||
node.spatial.get().is_some(),
|
|
||||||
"Internal: Node does not have a spatial attached!"
|
|
||||||
);
|
|
||||||
|
|
||||||
let handler = InputHandler {
|
let handler = InputHandler {
|
||||||
enabled: node.enabled.clone(),
|
enabled: node.enabled.clone(),
|
||||||
uid: node.uid.clone(),
|
uid: node.uid.clone(),
|
||||||
node: Arc::downgrade(node),
|
node: Arc::downgrade(node),
|
||||||
spatial: node.spatial.get().unwrap().clone(),
|
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
field: field.clone(),
|
field: field.clone(),
|
||||||
method_aliases: LifeLinkedNodeMap::default(),
|
method_aliases: LifeLinkedNodeMap::default(),
|
||||||
};
|
};
|
||||||
@@ -279,23 +304,30 @@ impl InputHandler {
|
|||||||
method.handle_new_handler(&handler);
|
method.handle_new_handler(&handler);
|
||||||
}
|
}
|
||||||
let handler = INPUT_HANDLER_REGISTRY.add(handler);
|
let handler = INPUT_HANDLER_REGISTRY.add(handler);
|
||||||
let _ = node.input_handler.set(handler);
|
node.add_aspect_raw(handler);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn find(client: &Client, path: &str) -> Result<Arc<Self>> {
|
fn find(client: &Client, path: &str) -> Result<Arc<Self>> {
|
||||||
InputHandler::get(&*client.get_node("Input Handler", path)?)
|
client.get_node("Input Handler", path)?.get_aspect::<Self>()
|
||||||
}
|
|
||||||
fn get(node: &Node) -> Result<Arc<Self>> {
|
|
||||||
node.get_aspect("Input Handler", "input handler", |n| &n.input_handler)
|
|
||||||
.cloned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, distance_link))]
|
#[instrument(level = "debug", skip(self, distance_link))]
|
||||||
fn send_input(&self, order: u32, distance_link: &DistanceLink, datamap: Datamap) {
|
fn send_input(
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
&self,
|
||||||
let _ = node.send_remote_signal("input", distance_link.serialize(order, datamap));
|
order: u32,
|
||||||
|
captured: bool,
|
||||||
|
distance_link: &DistanceLink,
|
||||||
|
datamap: Datamap,
|
||||||
|
) {
|
||||||
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let _ = node.send_remote_signal("input", distance_link.serialize(order, captured, datamap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for InputHandler {
|
||||||
|
const NAME: &'static str = "InputHandler";
|
||||||
|
}
|
||||||
impl PartialEq for InputHandler {
|
impl PartialEq for InputHandler {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.spatial == other.spatial
|
self.spatial == other.spatial
|
||||||
@@ -311,7 +343,7 @@ impl Drop for InputHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
||||||
let node = Node::create(client, "", "input", false);
|
let node = Node::create_path(client, "/input", false);
|
||||||
node.add_local_signal("create_input_handler", create_input_handler_flex);
|
node.add_local_signal("create_input_handler", create_input_handler_flex);
|
||||||
node.add_local_signal("create_input_method_pointer", pointer::create_pointer_flex);
|
node.add_local_signal("create_input_method_pointer", pointer::create_pointer_flex);
|
||||||
node.add_local_signal("create_input_method_tip", tip::create_tip_flex);
|
node.add_local_signal("create_input_method_tip", tip::create_tip_flex);
|
||||||
@@ -319,7 +351,7 @@ pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_input_handler_flex(
|
pub fn create_input_handler_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -331,13 +363,15 @@ pub fn create_input_handler_flex(
|
|||||||
field_path: &'a str,
|
field_path: &'a str,
|
||||||
}
|
}
|
||||||
let info: CreateInputHandlerInfo = deserialize(message.as_ref())?;
|
let info: CreateInputHandlerInfo = deserialize(message.as_ref())?;
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = calling_client
|
||||||
|
.get_node("Spatial parent", info.parent_path)?
|
||||||
|
.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = parse_transform(info.transform, true, true, true);
|
||||||
let field = find_field(&calling_client, info.field_path)?;
|
let field = find_field(&calling_client, info.field_path)?;
|
||||||
|
|
||||||
let node =
|
let node = Node::create_parent_name(&calling_client, "/input/handler", info.name, true)
|
||||||
Node::create(&calling_client, "/input/handler", info.name, true).add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
InputHandler::add_to(&node, &field)?;
|
InputHandler::add_to(&node, &field)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -355,7 +389,7 @@ pub fn process_input() {
|
|||||||
const LIMIT: usize = 50;
|
const LIMIT: usize = 50;
|
||||||
for method in methods {
|
for method in methods {
|
||||||
for alias in method.node.upgrade().unwrap().aliases.get_valid_contents() {
|
for alias in method.node.upgrade().unwrap().aliases.get_valid_contents() {
|
||||||
alias.enabled.store(false, Ordering::Relaxed);
|
alias.enabled.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_span!("Process input method").in_scope(|| {
|
debug_span!("Process input method").in_scope(|| {
|
||||||
@@ -396,23 +430,24 @@ pub fn process_input() {
|
|||||||
let captures = method.captures.take_valid_contents();
|
let captures = method.captures.take_valid_contents();
|
||||||
// Iterate over the distance links and send input to them
|
// Iterate over the distance links and send input to them
|
||||||
for (i, distance_link) in distance_links.into_iter().enumerate() {
|
for (i, distance_link) in distance_links.into_iter().enumerate() {
|
||||||
if i > LIMIT {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(method_alias) = distance_link
|
if let Some(method_alias) = distance_link
|
||||||
.handler
|
.handler
|
||||||
.method_aliases
|
.method_aliases
|
||||||
.get(&(Arc::as_ptr(&distance_link.method) as usize))
|
.get(&(Arc::as_ptr(&distance_link.method) as usize))
|
||||||
.and_then(|a| a.alias.get().cloned())
|
.and_then(|a| a.get_aspect::<Alias>().ok())
|
||||||
{
|
{
|
||||||
method_alias.enabled.store(true, Ordering::Relaxed);
|
method_alias.enabled.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
distance_link.send_input(i as u32, method.datamap.lock().clone().unwrap());
|
let captured = captures.contains(&distance_link.handler);
|
||||||
|
distance_link.send_input(
|
||||||
|
i as u32,
|
||||||
|
captured,
|
||||||
|
method.datamap.lock().clone().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
// If the current distance link is in the list of captured input handlers,
|
// If the current distance link is in the list of captured input handlers,
|
||||||
// break out of the loop to avoid sending input to the remaining distance links
|
// break out of the loop to avoid sending input to the remaining distance links
|
||||||
if captures.contains(&distance_link.handler) {
|
if captured {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ use super::{DistanceLink, InputSpecialization};
|
|||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::nodes::fields::{Field, Ray, RayMarchResult};
|
use crate::nodes::fields::{Field, Ray, RayMarchResult};
|
||||||
use crate::nodes::input::{InputMethod, InputType};
|
use crate::nodes::input::{InputMethod, InputType};
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::spatial::{parse_transform, Spatial, Transform};
|
||||||
use crate::nodes::{Message, Node};
|
use crate::nodes::{Message, Node};
|
||||||
use glam::{vec3, Mat4};
|
use glam::{vec3, Mat4};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::schemas::flat::{Datamap, InputDataType, Pointer as FlatPointer};
|
use stardust_xr::schemas::flat::{InputDataType, Pointer as FlatPointer};
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
use stardust_xr::schemas::flex::deserialize;
|
||||||
use stardust_xr::values::Transform;
|
use stardust_xr::values::Datamap;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -65,7 +66,7 @@ impl InputSpecialization for Pointer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_pointer_flex(
|
pub fn create_pointer_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> color_eyre::eyre::Result<()> {
|
) -> color_eyre::eyre::Result<()> {
|
||||||
@@ -77,16 +78,19 @@ pub fn create_pointer_flex(
|
|||||||
datamap: Option<Vec<u8>>,
|
datamap: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
let info: CreatePointerInfo = deserialize(message.as_ref())?;
|
let info: CreatePointerInfo = deserialize(message.as_ref())?;
|
||||||
let node = Node::create(&calling_client, "/input/method/pointer", info.name, true);
|
let node = Node::create_parent_name(&calling_client, "/input/method/pointer", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = calling_client
|
||||||
|
.get_node("Spatial parent", info.parent_path)?
|
||||||
|
.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
let node = node.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
InputMethod::add_to(
|
InputMethod::add_to(
|
||||||
&node,
|
&node,
|
||||||
InputType::Pointer(Pointer),
|
InputType::Pointer(Pointer),
|
||||||
info.datamap.and_then(|datamap| Datamap::new(datamap).ok()),
|
info.datamap
|
||||||
|
.and_then(|datamap| Datamap::from_raw(datamap).ok()),
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ use super::{DistanceLink, InputSpecialization};
|
|||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::nodes::fields::Field;
|
use crate::nodes::fields::Field;
|
||||||
use crate::nodes::input::{InputMethod, InputType};
|
use crate::nodes::input::{InputMethod, InputType};
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::spatial::{parse_transform, Spatial, Transform};
|
||||||
use crate::nodes::{Message, Node};
|
use crate::nodes::{Message, Node};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec3a, Mat4};
|
use glam::{vec3a, Mat4};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::schemas::flat::{Datamap, InputDataType, Tip as FlatTip};
|
use stardust_xr::schemas::flat::{InputDataType, Tip as FlatTip};
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
use stardust_xr::schemas::flex::deserialize;
|
||||||
use stardust_xr::values::Transform;
|
use stardust_xr::values::Datamap;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -17,8 +18,9 @@ pub struct Tip {
|
|||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
}
|
}
|
||||||
impl Tip {
|
impl Tip {
|
||||||
fn set_radius(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn set_radius(node: Arc<Node>, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
||||||
if let InputType::Tip(tip) = &mut *node.input_method.get().unwrap().specialization.lock() {
|
let input_method = node.get_aspect::<InputMethod>()?;
|
||||||
|
if let InputType::Tip(tip) = &mut *input_method.specialization.lock() {
|
||||||
tip.radius = deserialize(message.as_ref())?;
|
tip.radius = deserialize(message.as_ref())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -45,7 +47,11 @@ impl InputSpecialization for Tip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_tip_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_tip_flex(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateTipInfo<'a> {
|
struct CreateTipInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -55,18 +61,21 @@ pub fn create_tip_flex(_node: &Node, calling_client: Arc<Client>, message: Messa
|
|||||||
datamap: Option<Vec<u8>>,
|
datamap: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
let info: CreateTipInfo = deserialize(message.as_ref())?;
|
let info: CreateTipInfo = deserialize(message.as_ref())?;
|
||||||
let node = Node::create(&calling_client, "/input/method/tip", info.name, true);
|
let node = Node::create_parent_name(&calling_client, "/input/method/tip", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = calling_client
|
||||||
|
.get_node("Spatial parent", info.parent_path)?
|
||||||
|
.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|
||||||
let node = node.add_to_scenegraph()?;
|
let node = node.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, Some(parent), transform, false)?;
|
Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
InputMethod::add_to(
|
InputMethod::add_to(
|
||||||
&node,
|
&node,
|
||||||
InputType::Tip(Tip {
|
InputType::Tip(Tip {
|
||||||
radius: info.radius,
|
radius: info.radius,
|
||||||
}),
|
}),
|
||||||
info.datamap.and_then(|datamap| Datamap::new(datamap).ok()),
|
info.datamap
|
||||||
|
.and_then(|datamap| Datamap::from_raw(datamap).ok()),
|
||||||
)?;
|
)?;
|
||||||
node.add_local_signal("set_radius", Tip::set_radius);
|
node.add_local_signal("set_radius", Tip::set_radius);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
use super::{Item, ItemType};
|
use super::{Item, ItemType};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
buffers::BufferManager,
|
|
||||||
client::{Client, INTERNAL_CLIENT},
|
client::{Client, INTERNAL_CLIENT},
|
||||||
registry::Registry,
|
registry::Registry,
|
||||||
scenegraph::MethodResponseSender,
|
scenegraph::MethodResponseSender,
|
||||||
},
|
},
|
||||||
nodes::{
|
nodes::{
|
||||||
drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES, Drawable},
|
drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES},
|
||||||
items::TypeInfo,
|
items::TypeInfo,
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
spatial::{parse_transform, Spatial, Transform},
|
||||||
Message, Node,
|
Message, Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, Result};
|
use color_eyre::eyre::{bail, eyre, Result};
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mint::{RowMatrix4, Vector2};
|
use mint::{RowMatrix4, Vector2};
|
||||||
@@ -21,30 +20,17 @@ use nanoid::nanoid;
|
|||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use smithay::backend::{
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
allocator::{
|
use std::sync::Arc;
|
||||||
dmabuf::{Dmabuf, DmabufFlags},
|
|
||||||
Buffer,
|
|
||||||
},
|
|
||||||
renderer::ImportDma,
|
|
||||||
};
|
|
||||||
use stardust_xr::{
|
|
||||||
scenegraph::ScenegraphError,
|
|
||||||
schemas::flex::{deserialize, serialize},
|
|
||||||
values::{BufferInfo, Transform},
|
|
||||||
};
|
|
||||||
use std::{ffi::c_void, sync::Arc};
|
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
Color128, Material, Rect, RenderLayer, StereoKitDraw, Tex, TextureFormat, TextureType,
|
Color128, Material, Rect, RenderLayer, StereoKitDraw, Tex, TextureType, Transparency,
|
||||||
Transparency,
|
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc::{self, error::SendError};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub(super) static ref ITEM_TYPE_INFO_CAMERA: TypeInfo = TypeInfo {
|
pub(super) static ref ITEM_TYPE_INFO_CAMERA: TypeInfo = TypeInfo {
|
||||||
type_name: "camera",
|
type_name: "camera",
|
||||||
aliased_local_signals: vec!["apply_preview_material", "set_proj_matrix"],
|
aliased_local_signals: vec!["apply_preview_material", "frame"],
|
||||||
aliased_local_methods: vec!["render"],
|
aliased_local_methods: vec![],
|
||||||
aliased_remote_signals: vec![],
|
aliased_remote_signals: vec![],
|
||||||
ui: Default::default(),
|
ui: Default::default(),
|
||||||
items: Registry::new(),
|
items: Registry::new(),
|
||||||
@@ -54,7 +40,7 @@ lazy_static! {
|
|||||||
|
|
||||||
struct FrameInfo {
|
struct FrameInfo {
|
||||||
proj_matrix: Mat4,
|
proj_matrix: Mat4,
|
||||||
preview_size: Vector2<u32>,
|
px_size: Vector2<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CameraItem {
|
pub struct CameraItem {
|
||||||
@@ -62,139 +48,62 @@ pub struct CameraItem {
|
|||||||
frame_info: Mutex<FrameInfo>,
|
frame_info: Mutex<FrameInfo>,
|
||||||
sk_tex: OnceCell<Tex>,
|
sk_tex: OnceCell<Tex>,
|
||||||
sk_mat: OnceCell<Arc<Material>>,
|
sk_mat: OnceCell<Arc<Material>>,
|
||||||
render_requests_tx: mpsc::UnboundedSender<(Dmabuf, MethodResponseSender)>,
|
applied_to: Registry<ModelPart>,
|
||||||
render_requests_rx: Mutex<mpsc::UnboundedReceiver<(Dmabuf, MethodResponseSender)>>,
|
apply_to: Registry<ModelPart>,
|
||||||
rendered_notifiers: Mutex<Vec<MethodResponseSender>>,
|
|
||||||
applied_preview_to: Registry<ModelPart>,
|
|
||||||
apply_preview_to: Registry<ModelPart>,
|
|
||||||
}
|
}
|
||||||
impl CameraItem {
|
impl CameraItem {
|
||||||
pub fn add_to(node: &Arc<Node>, proj_matrix: Mat4, preview_size: Vector2<u32>) {
|
pub fn add_to(node: &Arc<Node>, proj_matrix: Mat4, px_size: Vector2<u32>) {
|
||||||
let (render_requests_tx, render_requests_rx) = mpsc::unbounded_channel();
|
|
||||||
let camera_specialization = CameraItem {
|
|
||||||
space: node.spatial.get().unwrap().clone(),
|
|
||||||
frame_info: Mutex::new(FrameInfo {
|
|
||||||
proj_matrix,
|
|
||||||
preview_size,
|
|
||||||
}),
|
|
||||||
sk_tex: OnceCell::new(),
|
|
||||||
sk_mat: OnceCell::new(),
|
|
||||||
render_requests_tx,
|
|
||||||
render_requests_rx: Mutex::new(render_requests_rx),
|
|
||||||
rendered_notifiers: Mutex::new(Vec::new()),
|
|
||||||
applied_preview_to: Registry::new(),
|
|
||||||
apply_preview_to: Registry::new(),
|
|
||||||
};
|
|
||||||
Item::add_to(
|
Item::add_to(
|
||||||
node,
|
node,
|
||||||
nanoid!(),
|
nanoid!(),
|
||||||
&ITEM_TYPE_INFO_CAMERA,
|
&ITEM_TYPE_INFO_CAMERA,
|
||||||
ItemType::Camera(camera_specialization),
|
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(),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
node.add_local_method("render", CameraItem::render_flex);
|
node.add_local_method("frame", CameraItem::frame_flex);
|
||||||
node.add_local_signal(
|
node.add_local_signal(
|
||||||
"apply_preview_material",
|
"apply_preview_material",
|
||||||
CameraItem::apply_preview_material_flex,
|
CameraItem::apply_preview_material_flex,
|
||||||
);
|
);
|
||||||
node.add_local_signal("set_proj_matrix", CameraItem::set_proj_matrix_flex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_flex(
|
fn frame_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
_message: Message,
|
||||||
response: MethodResponseSender,
|
response: MethodResponseSender,
|
||||||
) {
|
) {
|
||||||
let Some(item) = node.item.get() else {
|
response.wrap_sync(move || {
|
||||||
let _ = response.send(Err(ScenegraphError::MethodError {
|
let ItemType::Camera(_camera) = &node.get_aspect::<Item>().unwrap().specialization
|
||||||
error: "Item not found".to_string(),
|
else {
|
||||||
}));
|
return Err(eyre!("Wrong item type?"));
|
||||||
return;
|
};
|
||||||
};
|
Ok(serialize(())?.into())
|
||||||
let ItemType::Camera(camera) = &item.specialization else {
|
});
|
||||||
let _ = response.send(Err(ScenegraphError::MethodError {
|
|
||||||
error: "Wrong item type?".to_string(),
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let buffer_info: BufferInfo = match deserialize(&message.data) {
|
|
||||||
Ok(i) => i,
|
|
||||||
Err(e) => {
|
|
||||||
let _ = response.send(Err(ScenegraphError::MethodError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut builder = Dmabuf::builder(
|
|
||||||
(buffer_info.width as i32, buffer_info.height as i32),
|
|
||||||
buffer_info.fourcc.try_into().unwrap(),
|
|
||||||
DmabufFlags::from_bits_truncate(buffer_info.flags),
|
|
||||||
);
|
|
||||||
for (fd, plane) in message.fds.into_iter().zip(buffer_info.planes) {
|
|
||||||
builder.add_plane(
|
|
||||||
fd,
|
|
||||||
plane.idx,
|
|
||||||
plane.offset,
|
|
||||||
plane.stride,
|
|
||||||
plane.modifier.try_into().unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let buffer_to_render = builder.build().unwrap();
|
|
||||||
|
|
||||||
if let Err(SendError((_dmabuf, sender))) =
|
|
||||||
camera.render_requests_tx.send((buffer_to_render, response))
|
|
||||||
{
|
|
||||||
sender.send(Err(ScenegraphError::MethodError {
|
|
||||||
error: "Internal: sender broke????".to_string(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_preview_material_flex(
|
fn apply_preview_material_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(item) = node.item.get() else {
|
let ItemType::Camera(camera) = &node.get_aspect::<Item>().unwrap().specialization else {
|
||||||
bail!("Item not found?")
|
|
||||||
};
|
|
||||||
let ItemType::Camera(camera) = &item.specialization else {
|
|
||||||
bail!("Wrong item type?")
|
bail!("Wrong item type?")
|
||||||
};
|
};
|
||||||
|
let model_part_node =
|
||||||
let model_part_path = deserialize(&message.data)?;
|
calling_client.get_node("Model part", deserialize(&message.data).unwrap())?;
|
||||||
let model_part_node = calling_client.get_node("Model part", model_part_path)?;
|
let model_part = model_part_node.get_aspect::<ModelPart>()?;
|
||||||
let Drawable::ModelPart(model_part) =
|
camera.applied_to.add_raw(&model_part);
|
||||||
model_part_node.get_aspect("Model part", "model part", |n| &n.drawable)?
|
camera.apply_to.add_raw(&model_part);
|
||||||
else {
|
|
||||||
bail!("Drawable is not a model node")
|
|
||||||
};
|
|
||||||
|
|
||||||
camera.applied_preview_to.add_raw(model_part);
|
|
||||||
camera.apply_preview_to.add_raw(model_part);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_proj_matrix_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(item) = node.item.get() else {
|
|
||||||
bail!("Item not found?")
|
|
||||||
};
|
|
||||||
let ItemType::Camera(camera) = &item.specialization else {
|
|
||||||
bail!("Wrong item type?")
|
|
||||||
};
|
|
||||||
|
|
||||||
let proj_matrix: RowMatrix4<f32> = deserialize(&message.data)?;
|
|
||||||
|
|
||||||
let mut frame_info = camera.frame_info.lock();
|
|
||||||
frame_info.proj_matrix = proj_matrix.into();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,92 +111,33 @@ impl CameraItem {
|
|||||||
Ok(serialize(id)?.into())
|
Ok(serialize(id)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&self, sk: &impl StereoKitDraw, buffer_manager: &mut BufferManager) {
|
pub fn update(&self, sk: &impl StereoKitDraw) {
|
||||||
let frame_info = self.frame_info.lock();
|
let frame_info = self.frame_info.lock();
|
||||||
self.render_preview(sk, &*frame_info);
|
let sk_tex = self.sk_tex.get_or_init(|| {
|
||||||
self.render_dmabuf(sk, buffer_manager, &*frame_info);
|
sk.tex_gen_color(
|
||||||
}
|
Color128::default(),
|
||||||
|
frame_info.px_size.x as i32,
|
||||||
fn render_preview(&self, sk: &impl StereoKitDraw, frame_info: &FrameInfo) {
|
frame_info.px_size.y as i32,
|
||||||
if !self.apply_preview_to.is_empty() {
|
TextureType::RENDER_TARGET,
|
||||||
let sk_tex = self.sk_tex.get_or_init(|| {
|
stereokit::TextureFormat::RGBA32Linear,
|
||||||
sk.tex_gen_color(
|
)
|
||||||
Color128::default(),
|
});
|
||||||
frame_info.preview_size.x as i32,
|
let sk_mat = self.sk_mat.get_or_init(|| {
|
||||||
frame_info.preview_size.y as i32,
|
let shader = sk.shader_create_mem(&UNLIT_SHADER_BYTES).unwrap();
|
||||||
TextureType::RENDER_TARGET,
|
let mat = sk.material_create(&shader);
|
||||||
TextureFormat::RGBA32Linear,
|
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
|
||||||
)
|
sk.material_set_transparency(&mat, Transparency::Blend);
|
||||||
});
|
Arc::new(mat)
|
||||||
let sk_mat = self.sk_mat.get_or_init(|| {
|
});
|
||||||
let shader = sk.shader_create_mem(&UNLIT_SHADER_BYTES).unwrap();
|
for model_part in self.apply_to.take_valid_contents() {
|
||||||
let mat = sk.material_create(&shader);
|
model_part.replace_material(sk_mat.clone())
|
||||||
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
|
|
||||||
sk.material_set_transparency(&mat, Transparency::Blend);
|
|
||||||
Arc::new(mat)
|
|
||||||
});
|
|
||||||
for model_part in self.apply_preview_to.take_valid_contents() {
|
|
||||||
model_part.replace_material(sk_mat.clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.applied_preview_to.is_empty() {
|
if !self.applied_to.is_empty() {
|
||||||
sk.render_to(
|
|
||||||
self.sk_tex.get().unwrap(),
|
|
||||||
self.space.global_transform(),
|
|
||||||
frame_info.proj_matrix,
|
|
||||||
RenderLayer::all(),
|
|
||||||
stereokit::RenderClear::All,
|
|
||||||
Rect {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
w: 0.0,
|
|
||||||
h: 0.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_dmabuf(
|
|
||||||
&self,
|
|
||||||
sk: &impl StereoKitDraw,
|
|
||||||
buffer_manager: &mut BufferManager,
|
|
||||||
frame_info: &FrameInfo,
|
|
||||||
) {
|
|
||||||
let mut render_notifiers = self.rendered_notifiers.lock();
|
|
||||||
let mut render_requests_rx = self.render_requests_rx.lock();
|
|
||||||
while let Ok((buffer_to_render, render_response_sender)) = render_requests_rx.try_recv() {
|
|
||||||
let imported_dmabuf = buffer_manager
|
|
||||||
.renderer
|
|
||||||
.import_dmabuf(&buffer_to_render, None);
|
|
||||||
let smithay_tex = match imported_dmabuf {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(e) => {
|
|
||||||
let _ = render_response_sender.send(Err(ScenegraphError::MethodError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let sk_tex = sk.tex_create(TextureType::IMAGE_NO_MIPS, TextureFormat::RGBA32);
|
|
||||||
unsafe {
|
|
||||||
sk.tex_set_surface(
|
|
||||||
&sk_tex,
|
|
||||||
smithay_tex.tex_id() as usize as *mut c_void,
|
|
||||||
TextureType::RENDER_TARGET,
|
|
||||||
smithay::backend::renderer::gles::ffi::SRGB8_ALPHA8.into(),
|
|
||||||
buffer_to_render.size().w,
|
|
||||||
buffer_to_render.size().h,
|
|
||||||
1,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
sk.render_to(
|
sk.render_to(
|
||||||
sk_tex,
|
sk_tex,
|
||||||
self.space.global_transform(),
|
|
||||||
frame_info.proj_matrix,
|
frame_info.proj_matrix,
|
||||||
|
self.space.global_transform(),
|
||||||
RenderLayer::all(),
|
RenderLayer::all(),
|
||||||
stereokit::RenderClear::All,
|
stereokit::RenderClear::All,
|
||||||
Rect {
|
Rect {
|
||||||
@@ -297,37 +147,21 @@ impl CameraItem {
|
|||||||
h: 0.0,
|
h: 0.0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
render_notifiers.push(render_response_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_rendered(&self) {
|
|
||||||
for notifier in self.rendered_notifiers.lock().drain(..) {
|
|
||||||
let _ = notifier.send(Ok(Vec::new().into()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(sk: &impl StereoKitDraw, buffer_manager: &mut BufferManager) {
|
pub fn update(sk: &impl StereoKitDraw) {
|
||||||
for camera in ITEM_TYPE_INFO_CAMERA.items.get_valid_contents() {
|
for camera in ITEM_TYPE_INFO_CAMERA.items.get_valid_contents() {
|
||||||
let ItemType::Camera(camera) = &camera.specialization else {
|
let ItemType::Camera(camera) = &camera.specialization else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
camera.update(sk, buffer_manager);
|
camera.update(sk);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_rendered() {
|
|
||||||
for camera in ITEM_TYPE_INFO_CAMERA.items.get_valid_contents() {
|
|
||||||
let ItemType::Camera(camera) = &camera.specialization else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
camera.send_rendered();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn create_camera_item_flex(
|
pub(super) fn create_camera_item_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -337,21 +171,23 @@ pub(super) fn create_camera_item_flex(
|
|||||||
parent_path: &'a str,
|
parent_path: &'a str,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
proj_matrix: RowMatrix4<f32>,
|
proj_matrix: RowMatrix4<f32>,
|
||||||
preview_size: Vector2<u32>,
|
px_size: Vector2<u32>,
|
||||||
}
|
}
|
||||||
let info: CreateCameraItemInfo = deserialize(message.as_ref())?;
|
let info: CreateCameraItemInfo = deserialize(message.as_ref())?;
|
||||||
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_CAMERA.type_name);
|
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_CAMERA.type_name);
|
||||||
let space = find_spatial_parent(&calling_client, info.parent_path)?;
|
let space = calling_client
|
||||||
|
.get_node("Spatial parent", info.parent_path)?
|
||||||
|
.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|
||||||
let node =
|
let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, info.name, false)
|
||||||
Node::create(&INTERNAL_CLIENT, &parent_name, info.name, false).add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, None, transform * space.global_transform(), false)?;
|
Spatial::add_to(&node, None, transform * space.global_transform(), false);
|
||||||
CameraItem::add_to(&node, info.proj_matrix.into(), info.preview_size);
|
CameraItem::add_to(&node, info.proj_matrix.into(), info.px_size);
|
||||||
// TODO: this means ANY client can render anywhere with no limits, this needs to be changed!!
|
node.get_aspect::<Item>().unwrap().make_alias_named(
|
||||||
node.item
|
&calling_client,
|
||||||
.get()
|
&parent_name,
|
||||||
.unwrap()
|
info.name,
|
||||||
.make_alias_named(&calling_client, &parent_name, info.name)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
nodes::{
|
nodes::{
|
||||||
items::TypeInfo,
|
items::TypeInfo,
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
spatial::{parse_transform, Spatial, Transform},
|
||||||
Message, Node,
|
Message, Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -15,10 +15,7 @@ use color_eyre::eyre::{eyre, Result};
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::{
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
schemas::flex::{deserialize, serialize},
|
|
||||||
values::Transform,
|
|
||||||
};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -48,15 +45,17 @@ impl EnvironmentItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_path_flex(
|
fn get_path_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
_message: Message,
|
_message: Message,
|
||||||
response: MethodResponseSender,
|
response: MethodResponseSender,
|
||||||
) {
|
) {
|
||||||
response.wrap_sync(move || {
|
response.wrap_sync(move || {
|
||||||
let ItemType::Environment(environment_item) = &node.item.get().unwrap().specialization else {
|
let ItemType::Environment(environment_item) =
|
||||||
return Err(eyre!("Wrong item type?"))
|
&node.get_aspect::<Item>().unwrap().specialization
|
||||||
};
|
else {
|
||||||
|
return Err(eyre!("Wrong item type?"));
|
||||||
|
};
|
||||||
Ok(serialize(environment_item.path.as_str())?.into())
|
Ok(serialize(environment_item.path.as_str())?.into())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -67,7 +66,7 @@ impl EnvironmentItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn create_environment_item_flex(
|
pub(super) fn create_environment_item_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -80,16 +79,19 @@ pub(super) fn create_environment_item_flex(
|
|||||||
}
|
}
|
||||||
let info: CreateEnvironmentItemInfo = deserialize(message.as_ref())?;
|
let info: CreateEnvironmentItemInfo = deserialize(message.as_ref())?;
|
||||||
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_ENVIRONMENT.type_name);
|
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_ENVIRONMENT.type_name);
|
||||||
let space = find_spatial_parent(&calling_client, info.parent_path)?;
|
let space = calling_client
|
||||||
|
.get_node("Spatial parent", info.parent_path)?
|
||||||
|
.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|
||||||
let node =
|
let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, info.name, false)
|
||||||
Node::create(&INTERNAL_CLIENT, &parent_name, info.name, false).add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, None, transform * space.global_transform(), false)?;
|
Spatial::add_to(&node, None, transform * space.global_transform(), false);
|
||||||
EnvironmentItem::add_to(&node, info.item_data);
|
EnvironmentItem::add_to(&node, info.item_data);
|
||||||
node.item
|
node.get_aspect::<Item>().unwrap().make_alias_named(
|
||||||
.get()
|
&calling_client,
|
||||||
.unwrap()
|
&parent_name,
|
||||||
.make_alias_named(&calling_client, &parent_name, info.name)?;
|
info.name,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ use self::camera::CameraItem;
|
|||||||
use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT};
|
use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT};
|
||||||
use self::panel::{PanelItemTrait, ITEM_TYPE_INFO_PANEL};
|
use self::panel::{PanelItemTrait, ITEM_TYPE_INFO_PANEL};
|
||||||
use super::fields::Field;
|
use super::fields::Field;
|
||||||
use super::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use super::spatial::{parse_transform, Spatial};
|
||||||
use super::{Alias, Message, Node};
|
use super::{Alias, Aspect, Message, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::node_collections::LifeLinkedNodeMap;
|
use crate::core::node_collections::LifeLinkedNodeMap;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::nodes::alias::AliasInfo;
|
use crate::nodes::alias::AliasInfo;
|
||||||
use crate::nodes::fields::find_field;
|
use crate::nodes::fields::find_field;
|
||||||
|
use crate::nodes::spatial::Transform;
|
||||||
use color_eyre::eyre::{ensure, eyre, Result};
|
use color_eyre::eyre::{ensure, eyre, Result};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
@@ -20,7 +21,7 @@ use parking_lot::Mutex;
|
|||||||
use portable_atomic::Ordering;
|
use portable_atomic::Ordering;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ impl Item {
|
|||||||
if let Some(ui) = type_info.ui.lock().upgrade() {
|
if let Some(ui) = type_info.ui.lock().upgrade() {
|
||||||
ui.handle_create_item(&item);
|
ui.handle_create_item(&item);
|
||||||
}
|
}
|
||||||
let _ = node.item.set(item.clone());
|
node.add_aspect_raw(item.clone());
|
||||||
|
|
||||||
// if let Some(auto_acceptor) = node.get_client().and_then(|client| {
|
// if let Some(auto_acceptor) = node.get_client().and_then(|client| {
|
||||||
// client
|
// client
|
||||||
@@ -155,13 +156,20 @@ impl Item {
|
|||||||
self.make_alias_named(client, parent, &self.uid)
|
self.make_alias_named(client, parent, &self.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release_flex(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
fn release_flex(
|
||||||
let item = node.get_aspect("Item", "item", |n| &n.item)?;
|
node: Arc<Node>,
|
||||||
release(item);
|
_calling_client: Arc<Client>,
|
||||||
|
_message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
|
let item = node.get_aspect::<Item>()?;
|
||||||
|
release(&item);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for Item {
|
||||||
|
const NAME: &'static str = "Item";
|
||||||
|
}
|
||||||
impl Drop for Item {
|
impl Drop for Item {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.type_info.items.remove(self);
|
self.type_info.items.remove(self);
|
||||||
@@ -219,7 +227,7 @@ impl ItemUI {
|
|||||||
acceptor_field_aliases: Default::default(),
|
acceptor_field_aliases: Default::default(),
|
||||||
});
|
});
|
||||||
*type_info.ui.lock() = Arc::downgrade(&ui);
|
*type_info.ui.lock() = Arc::downgrade(&ui);
|
||||||
let _ = node.item_ui.set(ui.clone());
|
node.add_aspect_raw(ui.clone());
|
||||||
|
|
||||||
for item in type_info.items.get_valid_contents() {
|
for item in type_info.items.get_valid_contents() {
|
||||||
ui.handle_create_item(&item);
|
ui.handle_create_item(&item);
|
||||||
@@ -230,7 +238,9 @@ impl ItemUI {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn send_state(&self, state: &str, name: &str) {
|
fn send_state(&self, state: &str, name: &str) {
|
||||||
let Ok(serialized_data) = serialize(name) else {return};
|
let Ok(serialized_data) = serialize(name) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = self
|
let _ = self
|
||||||
.node
|
.node
|
||||||
.upgrade()
|
.upgrade()
|
||||||
@@ -239,14 +249,20 @@ impl ItemUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_create_item(&self, item: &Item) {
|
fn handle_create_item(&self, item: &Item) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
let Some(client) = node.get_client() else {return};
|
return;
|
||||||
|
};
|
||||||
|
let Some(client) = node.get_client() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(alias_node) = item.make_alias(&client, &(node.get_path().to_string() + "/item")) {
|
if let Ok(alias_node) = item.make_alias(&client, &(node.get_path().to_string() + "/item")) {
|
||||||
self.item_aliases.add(item.uid.clone(), &alias_node);
|
self.item_aliases.add(item.uid.clone(), &alias_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("create_item", serialized_data);
|
let _ = node.send_remote_signal("create_item", serialized_data);
|
||||||
}
|
}
|
||||||
fn handle_destroy_item(&self, item: &Item) {
|
fn handle_destroy_item(&self, item: &Item) {
|
||||||
@@ -254,29 +270,45 @@ impl ItemUI {
|
|||||||
self.send_state("destroy_item", item.uid.as_str());
|
self.send_state("destroy_item", item.uid.as_str());
|
||||||
}
|
}
|
||||||
fn handle_capture_item(&self, item: &Item, acceptor: &ItemAcceptor) {
|
fn handle_capture_item(&self, item: &Item, acceptor: &ItemAcceptor) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let Ok(message) = serialize((item.uid.as_str(), acceptor.uid.as_str())) else {return};
|
let Ok(message) = serialize((item.uid.as_str(), acceptor.uid.as_str())) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("capture_item", message);
|
let _ = node.send_remote_signal("capture_item", message);
|
||||||
}
|
}
|
||||||
fn handle_release_item(&self, item: &Item, acceptor: &ItemAcceptor) {
|
fn handle_release_item(&self, item: &Item, acceptor: &ItemAcceptor) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let Ok(message) = serialize((item.uid.as_str(), acceptor.uid.as_str())) else {return};
|
let Ok(message) = serialize((item.uid.as_str(), acceptor.uid.as_str())) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("release_item", message);
|
let _ = node.send_remote_signal("release_item", message);
|
||||||
}
|
}
|
||||||
fn handle_create_acceptor(&self, acceptor: &ItemAcceptor) {
|
fn handle_create_acceptor(&self, acceptor: &ItemAcceptor) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
let Some(client) = node.get_client() else {return};
|
return;
|
||||||
|
};
|
||||||
|
let Some(client) = node.get_client() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let Ok((alias, field_alias)) = acceptor.make_aliases(
|
let Ok((alias, field_alias)) = acceptor.make_aliases(
|
||||||
&client,
|
&client,
|
||||||
&format!("/item/{}/acceptor", self.type_info.type_name),
|
&format!("/item/{}/acceptor", self.type_info.type_name),
|
||||||
) else {return};
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.acceptor_aliases.add(acceptor.uid.clone(), &alias);
|
self.acceptor_aliases.add(acceptor.uid.clone(), &alias);
|
||||||
self.acceptor_field_aliases
|
self.acceptor_field_aliases
|
||||||
.add(acceptor.uid.clone(), &field_alias);
|
.add(acceptor.uid.clone(), &field_alias);
|
||||||
let Ok(message) = serialize(&acceptor.uid) else {return};
|
let Ok(message) = serialize(&acceptor.uid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("create_acceptor", message);
|
let _ = node.send_remote_signal("create_acceptor", message);
|
||||||
}
|
}
|
||||||
fn handle_destroy_acceptor(&self, acceptor: &ItemAcceptor) {
|
fn handle_destroy_acceptor(&self, acceptor: &ItemAcceptor) {
|
||||||
@@ -285,6 +317,9 @@ impl ItemUI {
|
|||||||
self.acceptor_field_aliases.remove(&acceptor.uid);
|
self.acceptor_field_aliases.remove(&acceptor.uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for ItemUI {
|
||||||
|
const NAME: &'static str = "Item";
|
||||||
|
}
|
||||||
impl Drop for ItemUI {
|
impl Drop for ItemUI {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
*self.type_info.ui.lock() = Weak::new();
|
*self.type_info.ui.lock() = Weak::new();
|
||||||
@@ -313,19 +348,19 @@ impl ItemAcceptor {
|
|||||||
if let Some(ui) = type_info.ui.lock().upgrade() {
|
if let Some(ui) = type_info.ui.lock().upgrade() {
|
||||||
ui.handle_create_acceptor(&acceptor);
|
ui.handle_create_acceptor(&acceptor);
|
||||||
}
|
}
|
||||||
let _ = node.item_acceptor.set(acceptor);
|
node.add_aspect_raw(acceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capture_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn capture_flex(node: Arc<Node>, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
||||||
if !node.enabled.load(Ordering::Relaxed) {
|
if !node.enabled.load(Ordering::Relaxed) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let acceptor = node.item_acceptor.get().unwrap();
|
let acceptor = node.get_aspect::<ItemAcceptor>().unwrap();
|
||||||
let item_path: &str = deserialize(message.as_ref())?;
|
let item_path: &str = deserialize(message.as_ref())?;
|
||||||
let item_node = calling_client.get_node("Item", item_path)?;
|
let item_node = calling_client.get_node("Item", item_path)?;
|
||||||
let item = item_node.get_aspect("Item", "item", |n| &n.item)?;
|
let item = item_node.get_aspect::<Item>()?;
|
||||||
capture(item, acceptor);
|
capture(&item, &acceptor);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -354,26 +389,39 @@ impl ItemAcceptor {
|
|||||||
Ok((acceptor_alias, acceptor_field_alias))
|
Ok((acceptor_alias, acceptor_field_alias))
|
||||||
}
|
}
|
||||||
fn handle_capture(&self, item: &Arc<Item>) {
|
fn handle_capture(&self, item: &Arc<Item>) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
let Some(client) = node.get_client() else {return};
|
return;
|
||||||
|
};
|
||||||
|
let Some(client) = node.get_client() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
self.accepted_registry.add_raw(item);
|
self.accepted_registry.add_raw(item);
|
||||||
if let Ok(alias_node) = item.make_alias(&client, &node.path) {
|
if let Ok(alias_node) = item.make_alias(&client, &node.path) {
|
||||||
self.accepted_aliases.add(item.uid.clone(), &alias_node);
|
self.accepted_aliases.add(item.uid.clone(), &alias_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("capture", serialized_data);
|
let _ = node.send_remote_signal("capture", serialized_data);
|
||||||
}
|
}
|
||||||
fn handle_release(&self, item: &Item) {
|
fn handle_release(&self, item: &Item) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
self.accepted_registry.remove(item);
|
self.accepted_registry.remove(item);
|
||||||
self.accepted_aliases.remove(&item.uid);
|
self.accepted_aliases.remove(&item.uid);
|
||||||
let Ok(message) = serialize(&item.uid) else {return};
|
let Ok(message) = serialize(&item.uid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("release", message);
|
let _ = node.send_remote_signal("release", message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for ItemAcceptor {
|
||||||
|
const NAME: &'static str = "ItemAcceptor";
|
||||||
|
}
|
||||||
impl Drop for ItemAcceptor {
|
impl Drop for ItemAcceptor {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.type_info.acceptors.remove(self);
|
self.type_info.acceptors.remove(self);
|
||||||
@@ -387,7 +435,7 @@ impl Drop for ItemAcceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
||||||
let node = Node::create(client, "", "item", false);
|
let node = Node::create_parent_name(client, "", "item", false);
|
||||||
node.add_local_signal("create_camera_item", camera::create_camera_item_flex);
|
node.add_local_signal("create_camera_item", camera::create_camera_item_flex);
|
||||||
node.add_local_signal(
|
node.add_local_signal(
|
||||||
"create_environment_item",
|
"create_environment_item",
|
||||||
@@ -408,7 +456,7 @@ fn type_info(name: &str) -> Result<&'static TypeInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_item_ui_flex(
|
pub fn register_item_ui_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -418,14 +466,14 @@ pub fn register_item_ui_flex(
|
|||||||
}
|
}
|
||||||
let info: RegisterItemUIInfo = deserialize(message.as_ref())?;
|
let info: RegisterItemUIInfo = deserialize(message.as_ref())?;
|
||||||
let type_info = type_info(info.item_type)?;
|
let type_info = type_info(info.item_type)?;
|
||||||
let ui =
|
let ui = Node::create_parent_name(&calling_client, "/item", type_info.type_name, true)
|
||||||
Node::create(&calling_client, "/item", type_info.type_name, true).add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
ItemUI::add_to(&ui, type_info)?;
|
ItemUI::add_to(&ui, type_info)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_item_acceptor_flex(
|
fn create_item_acceptor_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -438,19 +486,21 @@ fn create_item_acceptor_flex(
|
|||||||
item_type: &'a str,
|
item_type: &'a str,
|
||||||
}
|
}
|
||||||
let info: CreateItemAcceptorInfo = deserialize(message.as_ref())?;
|
let info: CreateItemAcceptorInfo = deserialize(message.as_ref())?;
|
||||||
let space = find_spatial_parent(&calling_client, info.parent_path)?;
|
let space = calling_client
|
||||||
|
.get_node("Reference space", info.parent_path)?
|
||||||
|
.get_aspect::<Spatial>()?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
let field = find_field(&calling_client, info.field_path)?;
|
let field = find_field(&calling_client, info.field_path)?;
|
||||||
let type_info = type_info(info.item_type)?;
|
let type_info = type_info(info.item_type)?;
|
||||||
|
|
||||||
let node = Node::create(
|
let node = Node::create_parent_name(
|
||||||
&calling_client,
|
&calling_client,
|
||||||
&format!("/item/{}/acceptor", type_info.type_name),
|
&format!("/item/{}/acceptor", type_info.type_name),
|
||||||
info.name,
|
info.name,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&node, Some(space), transform, false)?;
|
Spatial::add_to(&node, Some(space.clone()), transform, false);
|
||||||
ItemAcceptor::add_to(&node, type_info, field);
|
ItemAcceptor::add_to(&node, type_info, field);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ use crate::{
|
|||||||
registry::Registry,
|
registry::Registry,
|
||||||
},
|
},
|
||||||
nodes::{
|
nodes::{
|
||||||
drawable::{model::ModelPart, Drawable},
|
drawable::model::ModelPart,
|
||||||
items::{Item, ItemType, TypeInfo},
|
items::{Item, ItemType, TypeInfo},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Message, Node,
|
Message, Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
@@ -95,8 +95,8 @@ impl<'de> Visitor<'de> for SurfaceIDVisitor {
|
|||||||
|
|
||||||
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
||||||
let Some(discrim) = seq.next_element()? else {
|
let Some(discrim) = seq.next_element()? else {
|
||||||
return Err(A::Error::missing_field("discrim"));
|
return Err(A::Error::missing_field("discrim"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// idk if you wanna check for extraneous elements
|
// idk if you wanna check for extraneous elements
|
||||||
// I didn't bother
|
// I didn't bother
|
||||||
@@ -106,8 +106,8 @@ impl<'de> Visitor<'de> for SurfaceIDVisitor {
|
|||||||
"Toplevel" => Ok(SurfaceID::Toplevel),
|
"Toplevel" => Ok(SurfaceID::Toplevel),
|
||||||
"Child" => {
|
"Child" => {
|
||||||
let Some(text) = seq.next_element()? else {
|
let Some(text) = seq.next_element()? else {
|
||||||
return Err(A::Error::missing_field("child_text"));
|
return Err(A::Error::missing_field("child_text"));
|
||||||
};
|
};
|
||||||
Ok(SurfaceID::Child(text))
|
Ok(SurfaceID::Child(text))
|
||||||
}
|
}
|
||||||
_ => Err(A::Error::unknown_variant(
|
_ => Err(A::Error::unknown_variant(
|
||||||
@@ -203,7 +203,9 @@ pub trait Backend: Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
|
pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
|
||||||
let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None};
|
let ItemType::Panel(panel_item) = &node.get_aspect::<Item>().ok()?.specialization else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
Some(panel_item.clone())
|
Some(panel_item.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,10 +228,10 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
.and_then(|env| state(&env));
|
.and_then(|env| state(&env));
|
||||||
|
|
||||||
let uid = nanoid!();
|
let uid = nanoid!();
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
let node = Node::create_parent_name(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
||||||
.add_to_scenegraph()
|
.add_to_scenegraph()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
|
||||||
if let Some(startup_settings) = &startup_settings {
|
if let Some(startup_settings) = &startup_settings {
|
||||||
spatial.set_local_transform(startup_settings.root);
|
spatial.set_local_transform(startup_settings.root);
|
||||||
}
|
}
|
||||||
@@ -251,10 +253,7 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex);
|
node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex);
|
||||||
node.add_local_signal("close_toplevel", Self::close_toplevel_flex);
|
node.add_local_signal("close_toplevel", Self::close_toplevel_flex);
|
||||||
node.add_local_signal("auto_size_toplevel", Self::auto_size_toplevel_flex);
|
node.add_local_signal("auto_size_toplevel", Self::auto_size_toplevel_flex);
|
||||||
node.add_local_signal(
|
node.add_local_signal("set_toplevel_size", Self::set_toplevel_size_flex);
|
||||||
"set_toplevel_size_changed",
|
|
||||||
Self::set_toplevel_size_changed_flex,
|
|
||||||
);
|
|
||||||
|
|
||||||
node.add_local_signal("pointer_motion", Self::pointer_motion_flex);
|
node.add_local_signal("pointer_motion", Self::pointer_motion_flex);
|
||||||
node.add_local_signal("pointer_button", Self::pointer_button_flex);
|
node.add_local_signal("pointer_button", Self::pointer_button_flex);
|
||||||
@@ -270,7 +269,9 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
panel_item
|
panel_item
|
||||||
}
|
}
|
||||||
pub fn drop_toplevel(&self) {
|
pub fn drop_toplevel(&self) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
node.destroy();
|
node.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,60 +279,88 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
// Remote signals
|
// Remote signals
|
||||||
impl<B: Backend + ?Sized> PanelItem<B> {
|
impl<B: Backend + ?Sized> PanelItem<B> {
|
||||||
pub fn toplevel_parent_changed(&self, parent: &str) {
|
pub fn toplevel_parent_changed(&self, parent: &str) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("toplevel_parent_changed", serialize(parent).unwrap());
|
let _ = node.send_remote_signal("toplevel_parent_changed", serialize(parent).unwrap());
|
||||||
}
|
}
|
||||||
pub fn toplevel_title_changed(&self, title: &str) {
|
pub fn toplevel_title_changed(&self, title: &str) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("toplevel_title_changed", serialize(title).unwrap());
|
let _ = node.send_remote_signal("toplevel_title_changed", serialize(title).unwrap());
|
||||||
}
|
}
|
||||||
pub fn toplevel_app_id_changed(&self, app_id: &str) {
|
pub fn toplevel_app_id_changed(&self, app_id: &str) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap());
|
let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap());
|
||||||
}
|
}
|
||||||
pub fn toplevel_fullscreen_active(&self, active: bool) {
|
pub fn toplevel_fullscreen_active(&self, active: bool) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("toplevel_fullscreen_active", serialize(active).unwrap());
|
let _ = node.send_remote_signal("toplevel_fullscreen_active", serialize(active).unwrap());
|
||||||
}
|
}
|
||||||
pub fn toplevel_move_request(&self) {
|
pub fn toplevel_move_request(&self) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("toplevel_move_request", Vec::<u8>::new());
|
let _ = node.send_remote_signal("toplevel_move_request", Vec::<u8>::new());
|
||||||
}
|
}
|
||||||
pub fn toplevel_resize_request(&self, up: bool, down: bool, left: bool, right: bool) {
|
pub fn toplevel_resize_request(&self, up: bool, down: bool, left: bool, right: bool) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal(
|
let _ = node.send_remote_signal(
|
||||||
"toplevel_resize_request",
|
"toplevel_resize_request",
|
||||||
serialize((up, down, left, right)).unwrap(),
|
serialize((up, down, left, right)).unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pub fn toplevel_size_changed(&self, size: Vector2<u32>) {
|
pub fn toplevel_size_changed(&self, size: Vector2<u32>) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("toplevel_size_changed", serialize(size).unwrap());
|
let _ = node.send_remote_signal("toplevel_size_changed", serialize(size).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor(&self, geometry: Option<Geometry>) {
|
pub fn set_cursor(&self, geometry: Option<Geometry>) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap());
|
let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_child(&self, uid: &str, info: ChildInfo) {
|
pub fn new_child(&self, uid: &str, info: ChildInfo) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("new_child", serialize((uid, info)).unwrap());
|
let _ = node.send_remote_signal("new_child", serialize((uid, info)).unwrap());
|
||||||
}
|
}
|
||||||
pub fn reposition_child(&self, uid: &str, geometry: Geometry) {
|
pub fn reposition_child(&self, uid: &str, geometry: Geometry) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("reposition_child", serialize((uid, geometry)).unwrap());
|
let _ = node.send_remote_signal("reposition_child", serialize((uid, geometry)).unwrap());
|
||||||
}
|
}
|
||||||
pub fn drop_child(&self, uid: &str) {
|
pub fn drop_child(&self, uid: &str) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = node.send_remote_signal("drop_child", serialize(uid).unwrap());
|
let _ = node.send_remote_signal("drop_child", serialize(uid).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Local signals
|
// Local signals
|
||||||
macro_rules! flex_no_args {
|
macro_rules! flex_no_args {
|
||||||
($fn_name: ident, $trait_fn: ident) => {
|
($fn_name: ident, $trait_fn: ident) => {
|
||||||
fn $fn_name(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
fn $fn_name(
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
_message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
panel_item.$trait_fn();
|
panel_item.$trait_fn();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -339,8 +368,10 @@ macro_rules! flex_no_args {
|
|||||||
}
|
}
|
||||||
macro_rules! flex_deserialize {
|
macro_rules! flex_deserialize {
|
||||||
($fn_name: ident, $trait_fn: ident) => {
|
($fn_name: ident, $trait_fn: ident) => {
|
||||||
fn $fn_name(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn $fn_name(node: Arc<Node>, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
panel_item.$trait_fn(deserialize(message.as_ref())?);
|
panel_item.$trait_fn(deserialize(message.as_ref())?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -348,11 +379,13 @@ macro_rules! flex_deserialize {
|
|||||||
}
|
}
|
||||||
impl<B: Backend + ?Sized> PanelItem<B> {
|
impl<B: Backend + ?Sized> PanelItem<B> {
|
||||||
fn apply_surface_material_flex(
|
fn apply_surface_material_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct SurfaceMaterialInfo<'a> {
|
struct SurfaceMaterialInfo<'a> {
|
||||||
@@ -366,24 +399,26 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
.scenegraph
|
.scenegraph
|
||||||
.get_node(info.model_node_path)
|
.get_node(info.model_node_path)
|
||||||
.ok_or_else(|| eyre!("Model node not found"))?;
|
.ok_or_else(|| eyre!("Model node not found"))?;
|
||||||
let Some(Drawable::ModelPart(model_part)) = model_node.drawable.get() else {bail!("Node is not a model")};
|
let model_part = model_node.get_aspect::<ModelPart>()?;
|
||||||
debug!(?info, "Apply surface material");
|
debug!(?info, "Apply surface material");
|
||||||
|
|
||||||
panel_item.apply_surface_material(info.surface, model_part);
|
panel_item.apply_surface_material(info.surface, &model_part);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
flex_no_args!(close_toplevel_flex, close_toplevel);
|
flex_no_args!(close_toplevel_flex, close_toplevel);
|
||||||
flex_no_args!(auto_size_toplevel_flex, auto_size_toplevel);
|
flex_no_args!(auto_size_toplevel_flex, auto_size_toplevel);
|
||||||
flex_deserialize!(set_toplevel_size_changed_flex, set_toplevel_size);
|
flex_deserialize!(set_toplevel_size_flex, set_toplevel_size);
|
||||||
|
|
||||||
fn pointer_motion_flex(
|
fn pointer_motion_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let (surface_id, position): (SurfaceID, Vector2<f32>) = deserialize(message.as_ref())?;
|
let (surface_id, position): (SurfaceID, Vector2<f32>) = deserialize(message.as_ref())?;
|
||||||
debug!(?surface_id, ?position, "Pointer deactivate");
|
debug!(?surface_id, ?position, "Pointer deactivate");
|
||||||
@@ -393,11 +428,13 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn pointer_button_flex(
|
fn pointer_button_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(message.as_ref())?;
|
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(message.as_ref())?;
|
||||||
debug!(?surface_id, button, state, "Pointer button");
|
debug!(?surface_id, button, state, "Pointer button");
|
||||||
@@ -406,11 +443,13 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn pointer_scroll_flex(
|
fn pointer_scroll_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct PointerScrollInfo {
|
struct PointerScrollInfo {
|
||||||
@@ -427,11 +466,13 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn keyboard_keys_flex(
|
fn keyboard_keys_flex(
|
||||||
node: &Node,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
let (surface_id, keymap_id, keys): (SurfaceID, &str, Vec<i32>) =
|
let (surface_id, keymap_id, keys): (SurfaceID, &str, Vec<i32>) =
|
||||||
deserialize(message.as_ref())?;
|
deserialize(message.as_ref())?;
|
||||||
debug!(?keys, "Set keyboard key state");
|
debug!(?keys, "Set keyboard key state");
|
||||||
@@ -441,14 +482,22 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
|
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let Ok(message) = serialize(sid) else {return};
|
let Ok(message) = serialize(sid) else { return };
|
||||||
let _ = node.send_remote_signal("grab_keyboard", message);
|
let _ = node.send_remote_signal("grab_keyboard", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn touch_down_flex(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn touch_down_flex(
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let (surface_id, id, position): (SurfaceID, u32, Vector2<f32>) =
|
let (surface_id, id, position): (SurfaceID, u32, Vector2<f32>) =
|
||||||
deserialize(message.as_ref())?;
|
deserialize(message.as_ref())?;
|
||||||
@@ -458,8 +507,14 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn touch_move_flex(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn touch_move_flex(
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
message: Message,
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(&node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let (id, position): (u32, Vector2<f32>) = deserialize(message.as_ref())?;
|
let (id, position): (u32, Vector2<f32>) = deserialize(message.as_ref())?;
|
||||||
debug!(?position, "Touch move");
|
debug!(?position, "Touch move");
|
||||||
|
|||||||
204
src/nodes/mod.rs
204
src/nodes/mod.rs
@@ -11,15 +11,15 @@ pub mod spatial;
|
|||||||
|
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use portable_atomic::{AtomicBool, Ordering};
|
use portable_atomic::{AtomicBool, Ordering};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use stardust_xr::messenger::MessageSenderHandle;
|
use stardust_xr::messenger::MessageSenderHandle;
|
||||||
use stardust_xr::scenegraph::ScenegraphError;
|
use stardust_xr::scenegraph::ScenegraphError;
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
|
use std::any::{Any, TypeId};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::future::Future;
|
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
@@ -29,14 +29,6 @@ use crate::core::registry::Registry;
|
|||||||
use crate::core::scenegraph::MethodResponseSender;
|
use crate::core::scenegraph::MethodResponseSender;
|
||||||
|
|
||||||
use self::alias::Alias;
|
use self::alias::Alias;
|
||||||
use self::audio::Sound;
|
|
||||||
use self::data::{PulseReceiver, PulseSender};
|
|
||||||
use self::drawable::Drawable;
|
|
||||||
use self::fields::Field;
|
|
||||||
use self::input::{InputHandler, InputMethod};
|
|
||||||
use self::items::{Item, ItemAcceptor, ItemUI};
|
|
||||||
use self::spatial::zone::Zone;
|
|
||||||
use self::spatial::Spatial;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
@@ -57,8 +49,10 @@ impl AsRef<[u8]> for Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Signal = fn(&Node, Arc<Client>, Message) -> Result<()>;
|
pub type Signal = fn(Arc<Node>, Arc<Client>, Message) -> Result<()>;
|
||||||
pub type Method = fn(&Node, Arc<Client>, Message, MethodResponseSender);
|
pub type Method = fn(Arc<Node>, Arc<Client>, Message, MethodResponseSender);
|
||||||
|
|
||||||
|
stardust_xr_server_codegen::codegen_node_protocol!();
|
||||||
|
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub enabled: Arc<AtomicBool>,
|
pub enabled: Arc<AtomicBool>,
|
||||||
@@ -69,35 +63,10 @@ pub struct Node {
|
|||||||
// trailing_slash_pos: usize,
|
// trailing_slash_pos: usize,
|
||||||
local_signals: Mutex<FxHashMap<String, Signal>>,
|
local_signals: Mutex<FxHashMap<String, Signal>>,
|
||||||
local_methods: Mutex<FxHashMap<String, Method>>,
|
local_methods: Mutex<FxHashMap<String, Method>>,
|
||||||
destroyable: bool,
|
|
||||||
|
|
||||||
pub alias: OnceCell<Arc<Alias>>,
|
|
||||||
aliases: Registry<Alias>,
|
aliases: Registry<Alias>,
|
||||||
|
aspects: Aspects,
|
||||||
pub spatial: OnceCell<Arc<Spatial>>,
|
destroyable: bool,
|
||||||
pub field: OnceCell<Arc<Field>>,
|
|
||||||
pub zone: OnceCell<Arc<Zone>>,
|
|
||||||
|
|
||||||
// Data
|
|
||||||
pub pulse_sender: OnceCell<Arc<PulseSender>>,
|
|
||||||
pub pulse_receiver: OnceCell<Arc<PulseReceiver>>,
|
|
||||||
|
|
||||||
// Drawable
|
|
||||||
pub drawable: OnceCell<Drawable>,
|
|
||||||
|
|
||||||
// Input
|
|
||||||
pub input_method: OnceCell<Arc<InputMethod>>,
|
|
||||||
pub input_handler: OnceCell<Arc<InputHandler>>,
|
|
||||||
|
|
||||||
// Item
|
|
||||||
pub item: OnceCell<Arc<Item>>,
|
|
||||||
pub item_acceptor: OnceCell<Arc<ItemAcceptor>>,
|
|
||||||
pub item_ui: OnceCell<Arc<ItemUI>>,
|
|
||||||
|
|
||||||
// Sound
|
|
||||||
pub sound: OnceCell<Arc<Sound>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
pub fn get_client(&self) -> Option<Arc<Client>> {
|
pub fn get_client(&self) -> Option<Arc<Client>> {
|
||||||
self.client.upgrade()
|
self.client.upgrade()
|
||||||
@@ -109,39 +78,32 @@ impl Node {
|
|||||||
self.path.as_str()
|
self.path.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(client: &Arc<Client>, parent: &str, name: &str, destroyable: bool) -> Self {
|
pub fn create_parent_name(
|
||||||
|
client: &Arc<Client>,
|
||||||
|
parent: &str,
|
||||||
|
name: &str,
|
||||||
|
destroyable: bool,
|
||||||
|
) -> Self {
|
||||||
let mut path = parent.to_string();
|
let mut path = parent.to_string();
|
||||||
path.push('/');
|
path.push('/');
|
||||||
path.push_str(name);
|
path.push_str(name);
|
||||||
|
Self::create_path(client, path, destroyable)
|
||||||
|
}
|
||||||
|
pub fn create_path(client: &Arc<Client>, path: impl ToString, destroyable: bool) -> Self {
|
||||||
let node = Node {
|
let node = Node {
|
||||||
enabled: Arc::new(AtomicBool::new(true)),
|
enabled: Arc::new(AtomicBool::new(true)),
|
||||||
uid: nanoid!(),
|
uid: nanoid!(),
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
message_sender_handle: client.message_sender_handle.clone(),
|
message_sender_handle: client.message_sender_handle.clone(),
|
||||||
path,
|
path: path.to_string(),
|
||||||
// trailing_slash_pos: parent.len(),
|
// trailing_slash_pos: parent.len(),
|
||||||
local_signals: Default::default(),
|
local_signals: Default::default(),
|
||||||
local_methods: Default::default(),
|
local_methods: Default::default(),
|
||||||
|
aliases: Default::default(),
|
||||||
|
aspects: Default::default(),
|
||||||
destroyable,
|
destroyable,
|
||||||
|
|
||||||
alias: OnceCell::new(),
|
|
||||||
aliases: Registry::new(),
|
|
||||||
|
|
||||||
spatial: OnceCell::new(),
|
|
||||||
field: OnceCell::new(),
|
|
||||||
zone: OnceCell::new(),
|
|
||||||
pulse_sender: OnceCell::new(),
|
|
||||||
pulse_receiver: OnceCell::new(),
|
|
||||||
drawable: OnceCell::new(),
|
|
||||||
input_method: OnceCell::new(),
|
|
||||||
input_handler: OnceCell::new(),
|
|
||||||
item: OnceCell::new(),
|
|
||||||
item_acceptor: OnceCell::new(),
|
|
||||||
item_ui: OnceCell::new(),
|
|
||||||
sound: OnceCell::new(),
|
|
||||||
};
|
};
|
||||||
node.add_local_signal("set_enabled", Node::set_enabled_flex);
|
<Node as NodeAspect>::add_node_members(&node);
|
||||||
node.add_local_signal("destroy", Node::destroy_flex);
|
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
pub fn add_to_scenegraph(self) -> Result<Arc<Node>> {
|
pub fn add_to_scenegraph(self) -> Result<Arc<Node>> {
|
||||||
@@ -157,18 +119,9 @@ impl Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_enabled_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
node.enabled
|
|
||||||
.store(deserialize(message.as_ref())?, Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
// very much up for debate if we should allow this, as you can match objects using this
|
// very much up for debate if we should allow this, as you can match objects using this
|
||||||
// pub fn get_client_pid_flex(
|
// pub fn get_client_pid_flex(
|
||||||
// node: &Node,
|
// node: Arc<Node>,
|
||||||
// _calling_client: Arc<Client>,
|
// _calling_client: Arc<Client>,
|
||||||
// _message: Message,
|
// _message: Message,
|
||||||
// ) -> Result<Message> {
|
// ) -> Result<Message> {
|
||||||
@@ -179,16 +132,6 @@ impl Node {
|
|||||||
// let pid = client.pid.ok_or_else(|| eyre!("Client PID is unknown"))?;
|
// let pid = client.pid.ok_or_else(|| eyre!("Client PID is unknown"))?;
|
||||||
// Ok(serialize(pid)?.into())
|
// Ok(serialize(pid)?.into())
|
||||||
// }
|
// }
|
||||||
pub fn destroy_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
_message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
if node.destroyable {
|
|
||||||
node.destroy();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_local_signal(&self, name: &str, signal: Signal) {
|
pub fn add_local_signal(&self, name: &str, signal: Signal) {
|
||||||
self.local_signals.lock().insert(name.to_string(), signal);
|
self.local_signals.lock().insert(name.to_string(), signal);
|
||||||
@@ -197,27 +140,23 @@ impl Node {
|
|||||||
self.local_methods.lock().insert(name.to_string(), method);
|
self.local_methods.lock().insert(name.to_string(), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_aspect<F, T>(
|
pub fn add_aspect<A: Aspect>(&self, aspect: A) -> Arc<A> {
|
||||||
&self,
|
self.aspects.add(aspect)
|
||||||
node_name: &'static str,
|
}
|
||||||
aspect_type: &'static str,
|
pub fn add_aspect_raw<A: Aspect>(&self, aspect: Arc<A>) {
|
||||||
aspect_fn: F,
|
self.aspects.add_raw(aspect)
|
||||||
) -> Result<&T>
|
}
|
||||||
where
|
pub fn get_aspect<A: Aspect>(&self) -> Result<Arc<A>> {
|
||||||
F: FnOnce(&Node) -> &OnceCell<T>,
|
self.aspects.get()
|
||||||
{
|
|
||||||
aspect_fn(self)
|
|
||||||
.get()
|
|
||||||
.ok_or_else(|| eyre!("{} is not a {} node", node_name, aspect_type))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_local_signal(
|
pub fn send_local_signal(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
method: &str,
|
method: &str,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<(), ScenegraphError> {
|
) -> Result<(), ScenegraphError> {
|
||||||
if let Some(alias) = self.alias.get() {
|
if let Ok(alias) = self.get_aspect::<Alias>() {
|
||||||
if !alias.info.server_signals.iter().any(|e| e == &method) {
|
if !alias.info.server_signals.iter().any(|e| e == &method) {
|
||||||
return Err(ScenegraphError::SignalNotFound);
|
return Err(ScenegraphError::SignalNotFound);
|
||||||
}
|
}
|
||||||
@@ -239,13 +178,13 @@ impl Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn execute_local_method(
|
pub fn execute_local_method(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
method: &str,
|
method: &str,
|
||||||
message: Message,
|
message: Message,
|
||||||
response: MethodResponseSender,
|
response: MethodResponseSender,
|
||||||
) {
|
) {
|
||||||
if let Some(alias) = self.alias.get() {
|
if let Ok(alias) = self.get_aspect::<Alias>() {
|
||||||
if !alias.info.server_methods.iter().any(|e| e == &method) {
|
if !alias.info.server_methods.iter().any(|e| e == &method) {
|
||||||
response.send(Err(ScenegraphError::MethodNotFound));
|
response.send(Err(ScenegraphError::MethodNotFound));
|
||||||
return;
|
return;
|
||||||
@@ -295,29 +234,26 @@ impl Node {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn execute_remote_method(
|
pub async fn execute_remote_method_typed<S: Serialize, D: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
method: &str,
|
method: &str,
|
||||||
message: impl Into<Message>,
|
input: S,
|
||||||
) -> Result<impl Future<Output = Result<Message>>> {
|
fds: Vec<OwnedFd>,
|
||||||
let message = message.into();
|
) -> Result<(D, Vec<OwnedFd>)> {
|
||||||
let message_sender_handle = self
|
let message_sender_handle = self
|
||||||
.message_sender_handle
|
.message_sender_handle
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(eyre!("Messenger does not exist for this node"))?;
|
.ok_or(eyre!("Messenger does not exist for this node"))?;
|
||||||
|
|
||||||
let future =
|
let serialized = serialize(input)?;
|
||||||
message_sender_handle.method(self.path.as_str(), method, &message.data, message.fds)?;
|
let result = message_sender_handle
|
||||||
|
.method(self.path.as_str(), method, &serialized, fds)?
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?;
|
||||||
|
|
||||||
Ok(async {
|
let (message, fds) = result.into_components();
|
||||||
match future.await {
|
let deserialized: D = deserialize(&message)?;
|
||||||
Ok(m) => {
|
Ok((deserialized, fds))
|
||||||
let (data, fds) = m.into_components();
|
|
||||||
Ok(Message { data, fds })
|
|
||||||
}
|
|
||||||
Err(e) => Err(eyre!(e)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for Node {
|
impl Debug for Node {
|
||||||
@@ -328,8 +264,54 @@ impl Debug for Node {
|
|||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl NodeAspect for Node {
|
||||||
|
fn set_enabled(node: Arc<Node>, _calling_client: Arc<Client>, enabled: bool) -> Result<()> {
|
||||||
|
node.enabled.store(enabled, Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
|
||||||
|
if node.destroyable {
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Drop for Node {
|
impl Drop for Node {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Debug breakpoint
|
// Debug breakpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Aspect: Any + Send + Sync + 'static {
|
||||||
|
const NAME: &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Aspects(Mutex<FxHashMap<TypeId, Arc<dyn Any + Send + Sync + 'static>>>);
|
||||||
|
impl Aspects {
|
||||||
|
fn add<A: Aspect>(&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 get<A: Aspect + Any + Send + Sync + 'static>(&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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for Aspects {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.lock().clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,17 +4,18 @@ use crate::core::client::Client;
|
|||||||
use crate::core::client_state::{ClientState, ClientStateInternal};
|
use crate::core::client_state::{ClientState, ClientStateInternal};
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
use crate::core::scenegraph::MethodResponseSender;
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
use crate::wayland::WAYLAND_DISPLAY;
|
use crate::wayland::WAYLAND_DISPLAY;
|
||||||
|
#[cfg(feature = "xwayland")]
|
||||||
|
use crate::wayland::X_DISPLAY;
|
||||||
use crate::STARDUST_INSTANCE;
|
use crate::STARDUST_INSTANCE;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
use tracing::instrument;
|
|
||||||
|
|
||||||
use std::future::Future;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ pub struct Root {
|
|||||||
}
|
}
|
||||||
impl Root {
|
impl Root {
|
||||||
pub fn create(client: &Arc<Client>) -> Result<Arc<Self>> {
|
pub fn create(client: &Arc<Client>) -> Result<Arc<Self>> {
|
||||||
let node = Node::create(client, "", "", false);
|
let node = Node::create_parent_name(client, "", "", false);
|
||||||
node.add_local_signal("subscribe_frame", Root::subscribe_frame_flex);
|
node.add_local_signal("subscribe_frame", Root::subscribe_frame_flex);
|
||||||
node.add_local_signal("set_base_prefixes", Root::set_base_prefixes_flex);
|
node.add_local_signal("set_base_prefixes", Root::set_base_prefixes_flex);
|
||||||
node.add_local_method("state_token", Root::state_token_flex);
|
node.add_local_method("state_token", Root::state_token_flex);
|
||||||
@@ -42,7 +43,7 @@ impl Root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe_frame_flex(
|
fn subscribe_frame_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
_message: Message,
|
_message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -67,7 +68,7 @@ impl Root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_base_prefixes_flex(
|
fn set_base_prefixes_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -76,7 +77,7 @@ impl Root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn state_token_flex(
|
fn state_token_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
message: Message,
|
||||||
response: MethodResponseSender,
|
response: MethodResponseSender,
|
||||||
@@ -89,15 +90,16 @@ impl Root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_transform(&self, transform: Mat4) {
|
pub fn set_transform(&self, transform: Mat4) {
|
||||||
let spatial = self.node.spatial.get().unwrap();
|
let spatial = self.node.get_aspect::<Spatial>().unwrap();
|
||||||
spatial.set_spatial_parent(None).unwrap();
|
spatial.set_spatial_parent(None).unwrap();
|
||||||
spatial.set_local_transform(transform);
|
spatial.set_local_transform(transform);
|
||||||
}
|
}
|
||||||
pub fn save_state(&self) -> impl Future<Output = Result<ClientStateInternal>> {
|
pub async fn save_state(&self) -> Result<ClientStateInternal> {
|
||||||
let future = self
|
Ok(self
|
||||||
.node
|
.node
|
||||||
.execute_remote_method("save_state", Message::default());
|
.execute_remote_method_typed("save_state", (), Vec::new())
|
||||||
async move { Ok(deserialize(&future?.await?.data)?) }
|
.await?
|
||||||
|
.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +115,7 @@ macro_rules! var_env_insert {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn get_connection_environment_flex(
|
pub fn get_connection_environment_flex(
|
||||||
_node: &Node,
|
_node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
_message: Message,
|
_message: Message,
|
||||||
response: MethodResponseSender,
|
response: MethodResponseSender,
|
||||||
@@ -125,7 +127,10 @@ pub fn get_connection_environment_flex(
|
|||||||
{
|
{
|
||||||
var_env_insert!(env, WAYLAND_DISPLAY);
|
var_env_insert!(env, WAYLAND_DISPLAY);
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
var_env_insert!(env, DISPLAY);
|
env.insert(
|
||||||
|
"DISPLAY".to_string(),
|
||||||
|
format!(":{}", X_DISPLAY.get().unwrap()),
|
||||||
|
);
|
||||||
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
||||||
env.insert("QT_QPA_PLATFORM".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("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
|
||||||
|
|||||||
@@ -1,49 +1,66 @@
|
|||||||
pub mod zone;
|
pub mod zone;
|
||||||
|
|
||||||
use self::zone::{create_zone_flex, Zone};
|
use self::zone::Zone;
|
||||||
use super::{Message, Node};
|
use super::fields::Field;
|
||||||
|
use super::{Aspect, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
use crate::create_interface;
|
||||||
use color_eyre::eyre::{ensure, eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use glam::{vec3a, Mat4, Quat};
|
use glam::{vec3a, Mat4, Quat};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
|
||||||
use stardust_xr::values::Transform;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::{Arc, OnceLock, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use stereokit::{bounds_grow_to_fit_box, Bounds};
|
use stereokit::{bounds_grow_to_fit_box, Bounds};
|
||||||
|
|
||||||
|
stardust_xr_server_codegen::codegen_spatial_protocol!();
|
||||||
|
impl Transform {
|
||||||
|
pub fn to_mat4(self, position: bool, rotation: bool, scale: bool) -> Mat4 {
|
||||||
|
let position = position
|
||||||
|
.then_some(self.translation)
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| Vector3::from([0.0; 3]));
|
||||||
|
let rotation = rotation
|
||||||
|
.then_some(self.rotation)
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| Quat::IDENTITY.into());
|
||||||
|
let scale = scale
|
||||||
|
.then_some(self.scale)
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| Vector3::from([1.0; 3]));
|
||||||
|
|
||||||
|
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static ZONEABLE_REGISTRY: Registry<Spatial> = Registry::new();
|
static ZONEABLE_REGISTRY: Registry<Spatial> = Registry::new();
|
||||||
|
|
||||||
pub struct Spatial {
|
pub struct Spatial {
|
||||||
uid: String,
|
uid: String,
|
||||||
pub(super) node: Weak<Node>,
|
pub(super) node: Weak<Node>,
|
||||||
self_ref: Weak<Spatial>,
|
|
||||||
parent: Mutex<Option<Arc<Spatial>>>,
|
parent: Mutex<Option<Arc<Spatial>>>,
|
||||||
old_parent: Mutex<Option<Arc<Spatial>>>,
|
old_parent: Mutex<Option<Arc<Spatial>>>,
|
||||||
pub(super) transform: Mutex<Mat4>,
|
pub(super) transform: Mutex<Mat4>,
|
||||||
zone: Mutex<Weak<Zone>>,
|
zone: Mutex<Weak<Zone>>,
|
||||||
children: Registry<Spatial>,
|
children: Registry<Spatial>,
|
||||||
pub(super) bounding_box_calc: OnceLock<fn(&Node) -> Bounds>,
|
pub(super) bounding_box_calc: OnceCell<fn(&Node) -> Bounds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spatial {
|
impl Spatial {
|
||||||
pub fn new(node: Weak<Node>, parent: Option<Arc<Spatial>>, transform: Mat4) -> Arc<Self> {
|
pub fn new(node: Weak<Node>, parent: Option<Arc<Spatial>>, transform: Mat4) -> Arc<Self> {
|
||||||
Arc::new_cyclic(|self_ref| Spatial {
|
Arc::new(Spatial {
|
||||||
uid: nanoid!(),
|
uid: nanoid!(),
|
||||||
node,
|
node,
|
||||||
self_ref: self_ref.clone(),
|
|
||||||
parent: Mutex::new(parent),
|
parent: Mutex::new(parent),
|
||||||
old_parent: Mutex::new(None),
|
old_parent: Mutex::new(None),
|
||||||
transform: Mutex::new(transform),
|
transform: Mutex::new(transform),
|
||||||
zone: Mutex::new(Weak::new()),
|
zone: Mutex::new(Weak::new()),
|
||||||
children: Registry::new(),
|
children: Registry::new(),
|
||||||
bounding_box_calc: OnceLock::default(),
|
bounding_box_calc: OnceCell::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
@@ -51,29 +68,18 @@ impl Spatial {
|
|||||||
parent: Option<Arc<Spatial>>,
|
parent: Option<Arc<Spatial>>,
|
||||||
transform: Mat4,
|
transform: Mat4,
|
||||||
zoneable: bool,
|
zoneable: bool,
|
||||||
) -> Result<Arc<Spatial>> {
|
) -> Arc<Spatial> {
|
||||||
ensure!(
|
let spatial = Spatial::new(Arc::downgrade(node), parent.clone(), transform);
|
||||||
node.spatial.get().is_none(),
|
<Spatial as SpatialAspect>::add_node_members(node);
|
||||||
"Internal: Node already has a Spatial aspect!"
|
|
||||||
);
|
|
||||||
let spatial = Spatial::new(Arc::downgrade(node), parent, transform);
|
|
||||||
node.add_local_method("get_bounding_box", Spatial::get_bounding_box_flex);
|
|
||||||
node.add_local_method("get_transform", Spatial::get_transform_flex);
|
|
||||||
node.add_local_signal("set_transform", Spatial::set_transform_flex);
|
|
||||||
node.add_local_signal("set_spatial_parent", Spatial::set_spatial_parent_flex);
|
|
||||||
node.add_local_signal(
|
|
||||||
"set_spatial_parent_in_place",
|
|
||||||
Spatial::set_spatial_parent_in_place_flex,
|
|
||||||
);
|
|
||||||
node.add_local_signal("set_zoneable", Spatial::set_zoneable_flex);
|
|
||||||
node.add_local_method("field_distance", Spatial::field_distance_flex);
|
|
||||||
node.add_local_method("field_normal", Spatial::field_normal_flex);
|
|
||||||
node.add_local_method("field_closest_point", Spatial::field_closest_point_flex);
|
|
||||||
if zoneable {
|
if zoneable {
|
||||||
ZONEABLE_REGISTRY.add_raw(&spatial);
|
ZONEABLE_REGISTRY.add_raw(&spatial);
|
||||||
}
|
}
|
||||||
let _ = node.spatial.set(spatial.clone());
|
if let Some(parent) = parent {
|
||||||
Ok(spatial)
|
parent.children.add_raw(&spatial);
|
||||||
|
}
|
||||||
|
<Spatial as SpatialAspect>::add_node_members(node);
|
||||||
|
node.add_aspect_raw(spatial.clone());
|
||||||
|
spatial
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node(&self) -> Option<Arc<Node>> {
|
pub fn node(&self) -> Option<Arc<Node>> {
|
||||||
@@ -88,7 +94,9 @@ impl Spatial {
|
|||||||
|
|
||||||
// the output bounds are probably way bigger than they need to be
|
// the output bounds are probably way bigger than they need to be
|
||||||
pub fn get_bounding_box(&self) -> Bounds {
|
pub fn get_bounding_box(&self) -> Bounds {
|
||||||
let Some(node) = self.node() else {return Bounds::default()};
|
let Some(node) = self.node() else {
|
||||||
|
return Bounds::default();
|
||||||
|
};
|
||||||
let mut bounds = self
|
let mut bounds = self
|
||||||
.bounding_box_calc
|
.bounding_box_calc
|
||||||
.get()
|
.get()
|
||||||
@@ -108,10 +116,12 @@ impl Spatial {
|
|||||||
*self.transform.lock()
|
*self.transform.lock()
|
||||||
}
|
}
|
||||||
pub fn global_transform(&self) -> Mat4 {
|
pub fn global_transform(&self) -> Mat4 {
|
||||||
match self.get_parent() {
|
let parent_transform = self
|
||||||
Some(value) => value.global_transform() * *self.transform.lock(),
|
.get_parent()
|
||||||
None => *self.transform.lock(),
|
.as_deref()
|
||||||
}
|
.map(Self::global_transform)
|
||||||
|
.unwrap_or_default();
|
||||||
|
parent_transform * self.local_transform()
|
||||||
}
|
}
|
||||||
pub fn set_local_transform(&self, transform: Mat4) {
|
pub fn set_local_transform(&self, transform: Mat4) {
|
||||||
*self.transform.lock() = transform;
|
*self.transform.lock() = transform;
|
||||||
@@ -137,7 +147,7 @@ impl Spatial {
|
|||||||
let (mut reference_space_scl, mut reference_space_rot, mut reference_space_pos) =
|
let (mut reference_space_scl, mut reference_space_rot, mut reference_space_pos) =
|
||||||
local_transform_in_reference_space.to_scale_rotation_translation();
|
local_transform_in_reference_space.to_scale_rotation_translation();
|
||||||
|
|
||||||
if let Some(pos) = transform.position {
|
if let Some(pos) = transform.translation {
|
||||||
reference_space_pos = pos.into()
|
reference_space_pos = pos.into()
|
||||||
}
|
}
|
||||||
if let Some(rot) = transform.rotation {
|
if let Some(rot) = transform.rotation {
|
||||||
@@ -177,23 +187,21 @@ impl Spatial {
|
|||||||
fn get_parent(&self) -> Option<Arc<Spatial>> {
|
fn get_parent(&self) -> Option<Arc<Spatial>> {
|
||||||
self.parent.lock().clone()
|
self.parent.lock().clone()
|
||||||
}
|
}
|
||||||
fn set_parent(&self, new_parent: Option<Arc<Spatial>>) {
|
fn set_parent(self: &Arc<Self>, new_parent: Option<&Arc<Spatial>>) {
|
||||||
if let Some(parent) = self.get_parent() {
|
if let Some(parent) = self.get_parent() {
|
||||||
parent.children.remove(self);
|
parent.children.remove(&self);
|
||||||
}
|
}
|
||||||
if let Some(new_parent) = &new_parent {
|
if let Some(new_parent) = &new_parent {
|
||||||
new_parent
|
new_parent.children.add_raw(self);
|
||||||
.children
|
|
||||||
.add_raw(&self.self_ref.upgrade().unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.parent.lock() = new_parent;
|
*self.parent.lock() = new_parent.cloned();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_spatial_parent(&self, parent: Option<Arc<Spatial>>) -> Result<()> {
|
pub fn set_spatial_parent(self: &Arc<Self>, parent: Option<&Arc<Spatial>>) -> Result<()> {
|
||||||
let is_ancestor = parent
|
let is_ancestor = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|parent| self.is_ancestor_of(parent.clone()))
|
.map(|parent| self.is_ancestor_of((*parent).clone()))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
if is_ancestor {
|
if is_ancestor {
|
||||||
return Err(eyre!("Setting spatial parent would cause a loop"));
|
return Err(eyre!("Setting spatial parent would cause a loop"));
|
||||||
@@ -202,232 +210,27 @@ impl Spatial {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
pub fn set_spatial_parent_in_place(
|
||||||
pub fn set_spatial_parent_in_place(&self, parent: Option<Arc<Spatial>>) -> Result<()> {
|
self: &Arc<Self>,
|
||||||
|
parent: Option<&Arc<Spatial>>,
|
||||||
|
) -> Result<()> {
|
||||||
let is_ancestor = parent
|
let is_ancestor = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|parent| self.is_ancestor_of(parent.clone()))
|
.map(|parent| self.is_ancestor_of((*parent).clone()))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
if is_ancestor {
|
if is_ancestor {
|
||||||
return Err(eyre!("Setting spatial parent would cause a loop"));
|
return Err(eyre!("Setting spatial parent would cause a loop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_local_transform(Spatial::space_to_space_matrix(
|
self.set_local_transform(Spatial::space_to_space_matrix(
|
||||||
Some(self),
|
Some(&self),
|
||||||
parent.as_deref(),
|
parent.map(AsRef::as_ref),
|
||||||
));
|
));
|
||||||
self.set_parent(parent);
|
self.set_parent(parent);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bounding_box_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let this_spatial = node
|
|
||||||
.spatial
|
|
||||||
.get()
|
|
||||||
.ok_or_else(|| eyre!("Node doesn't have a spatial?"))?;
|
|
||||||
let relative_spatial_path: Option<&str> = deserialize(message.as_ref())?;
|
|
||||||
let bounds = if let Some(relative_spatial_path) = relative_spatial_path {
|
|
||||||
let relative_spatial =
|
|
||||||
find_reference_space(&calling_client, relative_spatial_path)?;
|
|
||||||
let center =
|
|
||||||
Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial))
|
|
||||||
.transform_point3([0.0; 3].into());
|
|
||||||
let bounds: Bounds = Bounds {
|
|
||||||
center,
|
|
||||||
dimensions: [0.0; 3].into(),
|
|
||||||
};
|
|
||||||
bounds_grow_to_fit_box(
|
|
||||||
bounds,
|
|
||||||
this_spatial.get_bounding_box(),
|
|
||||||
Some(Spatial::space_to_space_matrix(
|
|
||||||
Some(&this_spatial),
|
|
||||||
Some(&relative_spatial),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
this_spatial.get_bounding_box()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(serialize((
|
|
||||||
mint::Vector3::from(bounds.center),
|
|
||||||
mint::Vector3::from(bounds.dimensions),
|
|
||||||
))?
|
|
||||||
.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_transform_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let this_spatial = node
|
|
||||||
.spatial
|
|
||||||
.get()
|
|
||||||
.ok_or_else(|| eyre!("Node doesn't have a spatial?"))?;
|
|
||||||
let relative_spatial =
|
|
||||||
find_reference_space(&calling_client, deserialize(message.as_ref())?)?;
|
|
||||||
|
|
||||||
let (scale, rotation, position) = Spatial::space_to_space_matrix(
|
|
||||||
Some(this_spatial.as_ref()),
|
|
||||||
Some(relative_spatial.as_ref()),
|
|
||||||
)
|
|
||||||
.to_scale_rotation_translation();
|
|
||||||
|
|
||||||
Ok(serialize((
|
|
||||||
mint::Vector3::from(position),
|
|
||||||
mint::Quaternion::from(rotation),
|
|
||||||
mint::Vector3::from(scale),
|
|
||||||
))?
|
|
||||||
.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pub fn set_transform_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct TransformArgs<'a> {
|
|
||||||
reference_space_path: Option<&'a str>,
|
|
||||||
transform: Transform,
|
|
||||||
}
|
|
||||||
let transform_args: TransformArgs = deserialize(message.as_ref())?;
|
|
||||||
let reference_space_transform = transform_args
|
|
||||||
.reference_space_path
|
|
||||||
.map(|path| find_reference_space(&calling_client, path))
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
node.spatial.get().unwrap().set_local_transform_components(
|
|
||||||
reference_space_transform.as_deref(),
|
|
||||||
transform_args.transform,
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn set_spatial_parent_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let parent = find_spatial_parent(&calling_client, deserialize(message.as_ref())?)?;
|
|
||||||
node.spatial.get().unwrap().set_spatial_parent(Some(parent))
|
|
||||||
}
|
|
||||||
pub fn set_spatial_parent_in_place_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let parent = find_spatial_parent(&calling_client, deserialize(message.as_ref())?)?;
|
|
||||||
node.spatial
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.set_spatial_parent_in_place(Some(parent))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn set_zoneable_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let zoneable: bool = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
if zoneable {
|
|
||||||
ZONEABLE_REGISTRY.add_raw(spatial);
|
|
||||||
} else {
|
|
||||||
ZONEABLE_REGISTRY.remove(spatial);
|
|
||||||
zone::release(spatial);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn field_distance_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
|
|
||||||
let output = fields
|
|
||||||
.into_iter()
|
|
||||||
.map(|f| {
|
|
||||||
calling_client
|
|
||||||
.get_node("Field", f?)
|
|
||||||
.ok()?
|
|
||||||
.get_aspect("Field", "field", |n| &n.field)
|
|
||||||
.ok()
|
|
||||||
.cloned()
|
|
||||||
})
|
|
||||||
.map(|f| f.map(|f| f.distance(spatial, point.into())))
|
|
||||||
.collect::<Vec<Option<f32>>>();
|
|
||||||
|
|
||||||
Ok(serialize(output)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pub fn field_normal_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
|
|
||||||
let output = fields
|
|
||||||
.into_iter()
|
|
||||||
.map(|f| {
|
|
||||||
calling_client
|
|
||||||
.get_node("Field", f?)
|
|
||||||
.ok()?
|
|
||||||
.get_aspect("Field", "field", |n| &n.field)
|
|
||||||
.ok()
|
|
||||||
.cloned()
|
|
||||||
})
|
|
||||||
.map(|f| f.map(|f| Vector3::from(f.normal(spatial, point.into(), 0.001))))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Ok(serialize(output)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pub fn field_closest_point_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
|
|
||||||
let output = fields
|
|
||||||
.into_iter()
|
|
||||||
.map(|f| {
|
|
||||||
calling_client
|
|
||||||
.get_node("Field", f?)
|
|
||||||
.ok()?
|
|
||||||
.get_aspect("Field", "field", |n| &n.field)
|
|
||||||
.ok()
|
|
||||||
.cloned()
|
|
||||||
})
|
|
||||||
.map(|f| f.map(|f| Vector3::from(f.closest_point(spatial, point.into(), 0.001))))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Ok(serialize(output)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(self) fn zone_distance(&self) -> f32 {
|
pub(self) fn zone_distance(&self) -> f32 {
|
||||||
self.zone
|
self.zone
|
||||||
.lock()
|
.lock()
|
||||||
@@ -437,6 +240,128 @@ impl Spatial {
|
|||||||
.unwrap_or(f32::MAX)
|
.unwrap_or(f32::MAX)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Aspect for Spatial {
|
||||||
|
const NAME: &'static str = "Spatial";
|
||||||
|
}
|
||||||
|
impl SpatialAspect 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: mint::Vector3::from(bounds.center),
|
||||||
|
size: mint::Vector3::from(bounds.dimensions),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 bounds = bounds_grow_to_fit_box(
|
||||||
|
Bounds {
|
||||||
|
center,
|
||||||
|
dimensions: [0.0; 3].into(),
|
||||||
|
},
|
||||||
|
this_spatial.get_bounding_box(),
|
||||||
|
Some(Spatial::space_to_space_matrix(
|
||||||
|
Some(&this_spatial),
|
||||||
|
Some(&relative_spatial),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(BoundingBox {
|
||||||
|
center: mint::Vector3::from(bounds.center),
|
||||||
|
size: mint::Vector3::from(bounds.dimensions),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_local_transform(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
transform: Transform,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
this_spatial.set_local_transform_components(None, transform);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn set_relative_transform(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
relative_to: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
let relative_spatial = relative_to.get_aspect::<Spatial>()?;
|
||||||
|
|
||||||
|
this_spatial.set_local_transform_components(Some(&relative_spatial), transform);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_spatial_parent(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
|
||||||
|
this_spatial.set_spatial_parent(Some(&parent))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_spatial_parent_in_place(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let this_spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
|
||||||
|
this_spatial.set_spatial_parent_in_place(Some(&parent))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_zoneable(node: Arc<Node>, _calling_client: Arc<Client>, zoneable: bool) -> Result<()> {
|
||||||
|
let spatial = node.get_aspect::<Spatial>()?;
|
||||||
|
if zoneable {
|
||||||
|
ZONEABLE_REGISTRY.add_raw(&spatial);
|
||||||
|
} else {
|
||||||
|
ZONEABLE_REGISTRY.remove(&spatial);
|
||||||
|
zone::release(&spatial);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl PartialEq for Spatial {
|
impl PartialEq for Spatial {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.uid == other.uid
|
self.uid == other.uid
|
||||||
@@ -454,14 +379,14 @@ impl Debug for Spatial {
|
|||||||
}
|
}
|
||||||
impl Drop for Spatial {
|
impl Drop for Spatial {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
zone::release_drop(self);
|
||||||
ZONEABLE_REGISTRY.remove(self);
|
ZONEABLE_REGISTRY.remove(self);
|
||||||
zone::release(self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_transform(transform: Transform, position: bool, rotation: bool, scale: bool) -> Mat4 {
|
pub fn parse_transform(transform: Transform, position: bool, rotation: bool, scale: bool) -> Mat4 {
|
||||||
let position = position
|
let position = position
|
||||||
.then_some(transform.position)
|
.then_some(transform.translation)
|
||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or_else(|| Vector3::from([0.0; 3]));
|
.unwrap_or_else(|| Vector3::from([0.0; 3]));
|
||||||
let rotation = rotation
|
let rotation = rotation
|
||||||
@@ -476,53 +401,41 @@ pub fn parse_transform(transform: Transform, position: bool, rotation: bool, sca
|
|||||||
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_spatial(
|
pub struct SpatialInterface;
|
||||||
calling_client: &Arc<Client>,
|
impl SpatialInterfaceAspect for SpatialInterface {
|
||||||
node_name: &'static str,
|
fn create_spatial(
|
||||||
node_path: &str,
|
_node: Arc<Node>,
|
||||||
) -> color_eyre::eyre::Result<Arc<Spatial>> {
|
calling_client: Arc<Client>,
|
||||||
calling_client
|
name: String,
|
||||||
.get_node(node_name, node_path)?
|
parent: Arc<Node>,
|
||||||
.get_aspect(node_name, "spatial", |n| &n.spatial)
|
|
||||||
.cloned()
|
|
||||||
}
|
|
||||||
pub fn find_spatial_parent(
|
|
||||||
calling_client: &Arc<Client>,
|
|
||||||
node_path: &str,
|
|
||||||
) -> color_eyre::eyre::Result<Arc<Spatial>> {
|
|
||||||
find_spatial(calling_client, "Spatial parent", node_path)
|
|
||||||
}
|
|
||||||
pub fn find_reference_space(
|
|
||||||
calling_client: &Arc<Client>,
|
|
||||||
node_path: &str,
|
|
||||||
) -> color_eyre::eyre::Result<Arc<Spatial>> {
|
|
||||||
find_spatial(calling_client, "Reference space", node_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|
||||||
let node = Node::create(client, "", "spatial", false);
|
|
||||||
node.add_local_signal("create_spatial", create_spatial_flex);
|
|
||||||
node.add_local_signal("create_zone", create_zone_flex);
|
|
||||||
node.add_to_scenegraph().map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_spatial_flex(
|
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateSpatialInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
zoneable: bool,
|
zoneable: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let transform = parse_transform(transform, true, true, true);
|
||||||
|
let node = Node::create_parent_name(&calling_client, "/spatial/spatial", &name, true)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&node, Some(parent.clone()), transform, zoneable);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn create_zone(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
name: String,
|
||||||
|
parent: Arc<Node>,
|
||||||
|
transform: Transform,
|
||||||
|
field: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let parent = parent.get_aspect::<Spatial>()?;
|
||||||
|
let transform = parse_transform(transform, true, true, false);
|
||||||
|
let field = field.get_aspect::<Field>()?;
|
||||||
|
|
||||||
|
let node = Node::create_parent_name(&calling_client, "/spatial/zone", &name, true)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
let space = Spatial::add_to(&node, Some(parent.clone()), transform, false);
|
||||||
|
Zone::add_to(&node, space, &field);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
let info: CreateSpatialInfo = deserialize(message.as_ref())?;
|
|
||||||
let node = Node::create(&calling_client, "/spatial/spatial", info.name, true);
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, Some(parent), transform, info.zoneable)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_interface!(SpatialInterface, SpatialInterfaceAspect, "/spatial");
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
use super::{find_spatial, Spatial, ZONEABLE_REGISTRY};
|
use super::{Spatial, ZoneAspect, ZONEABLE_REGISTRY};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{client::Client, registry::Registry},
|
core::{client::Client, registry::Registry},
|
||||||
nodes::{
|
nodes::{
|
||||||
alias::{Alias, AliasInfo},
|
alias::{Alias, AliasInfo},
|
||||||
fields::{find_field, Field},
|
fields::Field,
|
||||||
spatial::{find_spatial_parent, parse_transform},
|
Aspect, Node,
|
||||||
Message, Node,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use glam::vec3a;
|
use glam::vec3a;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
|
||||||
use stardust_xr::{
|
|
||||||
schemas::flex::{deserialize, serialize},
|
|
||||||
values::Transform,
|
|
||||||
};
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
|
pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
|
||||||
@@ -31,22 +24,34 @@ pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
|
|||||||
*spatial.old_parent.lock() = spatial.get_parent();
|
*spatial.old_parent.lock() = spatial.get_parent();
|
||||||
*spatial.zone.lock() = Arc::downgrade(zone);
|
*spatial.zone.lock() = Arc::downgrade(zone);
|
||||||
zone.captured.add_raw(spatial);
|
zone.captured.add_raw(spatial);
|
||||||
let Some(node) = zone.spatial.node.upgrade() else {return};
|
let Some(node) = zone.spatial.node.upgrade() else {
|
||||||
let Ok(message) = serialize(&spatial.uid) else {return};
|
return;
|
||||||
let _ = node.send_remote_signal("capture", message);
|
};
|
||||||
|
let _ = super::zone_client::capture(&node, &spatial.uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn release(spatial: &Spatial) {
|
pub fn release(spatial: &Arc<Spatial>) {
|
||||||
let _ = spatial.set_spatial_parent_in_place(spatial.old_parent.lock().take());
|
let _ = spatial.set_spatial_parent_in_place(spatial.old_parent.lock().take().as_ref());
|
||||||
let mut spatial_zone = spatial.zone.lock();
|
let mut spatial_zone = spatial.zone.lock();
|
||||||
if let Some(spatial_zone) = spatial_zone.upgrade() {
|
if let Some(spatial_zone) = spatial_zone.upgrade() {
|
||||||
let Some(node) = spatial_zone.spatial.node.upgrade() else {return};
|
let Some(node) = spatial_zone.spatial.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
spatial_zone.captured.remove(spatial);
|
spatial_zone.captured.remove(spatial);
|
||||||
let Ok(message) = serialize(&spatial.uid) else {return};
|
let _ = super::zone_client::release(&node, &spatial.uid);
|
||||||
let _ = node.send_remote_signal("release", message);
|
|
||||||
}
|
}
|
||||||
*spatial_zone = Weak::new();
|
*spatial_zone = Weak::new();
|
||||||
}
|
}
|
||||||
|
pub(super) fn release_drop(spatial: &Spatial) {
|
||||||
|
let spatial_zone = spatial.zone.lock();
|
||||||
|
if let Some(spatial_zone) = spatial_zone.upgrade() {
|
||||||
|
let Some(node) = spatial_zone.spatial.node.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
spatial_zone.captured.remove(spatial);
|
||||||
|
let _ = super::zone_client::release(&node, &spatial.uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Zone {
|
pub struct Zone {
|
||||||
spatial: Arc<Spatial>,
|
spatial: Arc<Spatial>,
|
||||||
@@ -62,33 +67,28 @@ impl Zone {
|
|||||||
zoneables: Mutex::new(FxHashMap::default()),
|
zoneables: Mutex::new(FxHashMap::default()),
|
||||||
captured: Registry::new(),
|
captured: Registry::new(),
|
||||||
});
|
});
|
||||||
node.add_local_signal("capture", Zone::capture_flex);
|
<Zone as ZoneAspect>::add_node_members(node);
|
||||||
node.add_local_signal("release", Zone::release_flex);
|
node.add_aspect_raw(zone.clone());
|
||||||
node.add_local_signal("update", Zone::update);
|
|
||||||
let _ = node.zone.set(zone.clone());
|
|
||||||
zone
|
zone
|
||||||
}
|
}
|
||||||
fn capture_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
}
|
||||||
let zone = node.zone.get().unwrap();
|
impl Aspect for Zone {
|
||||||
let capture_path: &str = deserialize(message.as_ref())?;
|
const NAME: &'static str = "Zone";
|
||||||
let spatial = find_spatial(&calling_client, "Spatial", capture_path)?;
|
}
|
||||||
capture(&spatial, zone);
|
impl ZoneAspect for Zone {
|
||||||
Ok(())
|
fn update(node: Arc<Node>, _calling_client: Arc<Client>) -> color_eyre::eyre::Result<()> {
|
||||||
}
|
let zone = node.get_aspect::<Zone>()?;
|
||||||
fn release_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
let Some(field) = zone.field.upgrade() else {
|
||||||
let capture_path: &str = deserialize(message.as_ref())?;
|
return Err(color_eyre::eyre::eyre!("Zone's field has been destroyed"));
|
||||||
let spatial = find_spatial(&calling_client, "Spatial", capture_path)?;
|
};
|
||||||
release(&spatial);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn update(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
|
||||||
let zone = node.zone.get().unwrap();
|
|
||||||
let Some(field) = zone.field.upgrade() else { return Err(color_eyre::eyre::eyre!("Zone's field has been destroyed")) };
|
|
||||||
let Some((zone_client, zone_node)) = zone
|
let Some((zone_client, zone_node)) = zone
|
||||||
.spatial
|
.spatial
|
||||||
.node
|
.node
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.and_then(|n| n.get_client().zip(Some(n))) else { return Err(color_eyre::eyre::eyre!("No client on node?")) };
|
.and_then(|n| n.get_client().zip(Some(n)))
|
||||||
|
else {
|
||||||
|
return Err(color_eyre::eyre::eyre!("No client on node?"));
|
||||||
|
};
|
||||||
let mut old_zoneables = zone.zoneables.lock();
|
let mut old_zoneables = zone.zoneables.lock();
|
||||||
for (_uid, zoneable) in old_zoneables.iter() {
|
for (_uid, zoneable) in old_zoneables.iter() {
|
||||||
zoneable.destroy();
|
zoneable.destroy();
|
||||||
@@ -130,17 +130,41 @@ impl Zone {
|
|||||||
})
|
})
|
||||||
.collect::<FxHashMap<String, Arc<Node>>>();
|
.collect::<FxHashMap<String, Arc<Node>>>();
|
||||||
|
|
||||||
for entered_uid in zoneables.keys().filter(|k| !old_zoneables.contains_key(*k)) {
|
for (uid, zoneable) in zoneables
|
||||||
node.send_remote_signal("enter", serialize(entered_uid)?)?;
|
.iter()
|
||||||
|
.filter(|(k, _)| !old_zoneables.contains_key(*k))
|
||||||
|
{
|
||||||
|
super::zone_client::enter(&node, uid, zoneable)?;
|
||||||
}
|
}
|
||||||
for left_uid in old_zoneables.keys().filter(|k| !zoneables.contains_key(*k)) {
|
for left_uid in old_zoneables.keys().filter(|k| !zoneables.contains_key(*k)) {
|
||||||
node.send_remote_signal("leave", serialize(left_uid)?)?;
|
super::zone_client::leave(&node, &left_uid)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
*old_zoneables = zoneables;
|
*old_zoneables = zoneables;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn capture(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
spatial: Arc<Node>,
|
||||||
|
) -> color_eyre::eyre::Result<()> {
|
||||||
|
let zone = node.get_aspect::<Zone>()?;
|
||||||
|
let spatial = spatial.get_aspect()?;
|
||||||
|
capture(&spatial, &zone);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(
|
||||||
|
_node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
spatial: Arc<Node>,
|
||||||
|
) -> color_eyre::eyre::Result<()> {
|
||||||
|
let spatial = spatial.get_aspect()?;
|
||||||
|
release(&spatial);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Zone {
|
impl Drop for Zone {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -149,23 +173,3 @@ impl Drop for Zone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_zone_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateZoneInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
field_path: &'a str,
|
|
||||||
}
|
|
||||||
let info: CreateZoneInfo = deserialize(message.as_ref())?;
|
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
let field = find_field(&calling_client, info.field_path)?;
|
|
||||||
|
|
||||||
let node =
|
|
||||||
Node::create(&calling_client, "/spatial/zone", info.name, true).add_to_scenegraph()?;
|
|
||||||
let space = Spatial::add_to(&node, Some(parent), transform, false)?;
|
|
||||||
Zone::add_to(&node, space, &field);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use color_eyre::eyre::Result;
|
|||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use stardust_xr::schemas::{flat::Datamap, flex::flexbuffers};
|
use stardust_xr::{schemas::flex::flexbuffers, values::Datamap};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::StereoKitMultiThread;
|
use stereokit::StereoKitMultiThread;
|
||||||
|
|
||||||
@@ -28,8 +28,9 @@ pub struct EyePointer {
|
|||||||
}
|
}
|
||||||
impl EyePointer {
|
impl EyePointer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
.add_to_scenegraph()?;
|
||||||
|
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
|
||||||
let pointer =
|
let pointer =
|
||||||
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
|
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ impl EyePointer {
|
|||||||
let mut map = fbb.start_map();
|
let mut map = fbb.start_map();
|
||||||
map.push("eye", 2);
|
map.push("eye", 2);
|
||||||
map.end_map();
|
map.end_map();
|
||||||
*self.pointer.datamap.lock() = Datamap::new(fbb.take_buffer()).ok();
|
*self.pointer.datamap.lock() = Datamap::from_raw(fbb.take_buffer()).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{client::INTERNAL_CLIENT, typed_datamap::TypedDatamap},
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
data::{mask_matches, Mask, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY},
|
data::{
|
||||||
fields::Ray,
|
mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY,
|
||||||
|
},
|
||||||
|
fields::{Field, Ray},
|
||||||
input::{pointer::Pointer, InputMethod, InputType},
|
input::{pointer::Pointer, InputMethod, InputType},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Node,
|
Node,
|
||||||
@@ -12,6 +14,7 @@ use color_eyre::eyre::Result;
|
|||||||
use glam::{vec2, vec3, Mat4, Vec2, Vec3};
|
use glam::{vec2, vec3, Mat4, Vec2, Vec3};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use stardust_xr::values::Datamap;
|
||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
use stereokit::{ray_from_mouse, ButtonState, Key, StereoKitMultiThread};
|
use stereokit::{ray_from_mouse, ButtonState, Key, StereoKitMultiThread};
|
||||||
use xkbcommon::xkb::{Context, Keymap, FORMAT_TEXT_V1};
|
use xkbcommon::xkb::{Context, Keymap, FORMAT_TEXT_V1};
|
||||||
@@ -19,9 +22,12 @@ use xkbcommon::xkb::{Context, Keymap, FORMAT_TEXT_V1};
|
|||||||
#[derive(Default, Deserialize, Serialize)]
|
#[derive(Default, Deserialize, Serialize)]
|
||||||
struct MouseEvent {
|
struct MouseEvent {
|
||||||
select: f32,
|
select: f32,
|
||||||
|
middle: f32,
|
||||||
|
context: f32,
|
||||||
grab: f32,
|
grab: f32,
|
||||||
scroll_continuous: Vec2,
|
scroll_continuous: Vec2,
|
||||||
scroll_discrete: Vec2,
|
scroll_discrete: Vec2,
|
||||||
|
raw_input_events: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
@@ -46,14 +52,15 @@ pub struct MousePointer {
|
|||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
spatial: Arc<Spatial>,
|
spatial: Arc<Spatial>,
|
||||||
pointer: Arc<InputMethod>,
|
pointer: Arc<InputMethod>,
|
||||||
mouse_datamap: TypedDatamap<MouseEvent>,
|
mouse_datamap: MouseEvent,
|
||||||
keyboard_datamap: TypedDatamap<KeyboardEvent>,
|
keyboard_datamap: KeyboardEvent,
|
||||||
keyboard_sender: Arc<PulseSender>,
|
keyboard_sender: Arc<PulseSender>,
|
||||||
}
|
}
|
||||||
impl MousePointer {
|
impl MousePointer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
.add_to_scenegraph()?;
|
||||||
|
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
|
||||||
let pointer =
|
let pointer =
|
||||||
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
|
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
|
||||||
|
|
||||||
@@ -64,8 +71,11 @@ impl MousePointer {
|
|||||||
.get_as_string(FORMAT_TEXT_V1),
|
.get_as_string(FORMAT_TEXT_V1),
|
||||||
);
|
);
|
||||||
|
|
||||||
let keyboard_sender =
|
let keyboard_sender = PulseSender::add_to(
|
||||||
PulseSender::add_to(&node, Mask::from_struct::<KeyboardEvent>()).unwrap();
|
&node,
|
||||||
|
Datamap::from_typed(KeyboardEvent::default()).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(MousePointer {
|
Ok(MousePointer {
|
||||||
node,
|
node,
|
||||||
@@ -96,7 +106,22 @@ impl MousePointer {
|
|||||||
} else {
|
} else {
|
||||||
0.0f32
|
0.0f32
|
||||||
};
|
};
|
||||||
self.mouse_datamap.grab = if sk.input_key(Key::MouseRight).contains(ButtonState::ACTIVE)
|
self.mouse_datamap.middle =
|
||||||
|
if sk.input_key(Key::MouseCenter).contains(ButtonState::ACTIVE) {
|
||||||
|
1.0f32
|
||||||
|
} else {
|
||||||
|
0.0f32
|
||||||
|
};
|
||||||
|
self.mouse_datamap.context =
|
||||||
|
if sk.input_key(Key::MouseRight).contains(ButtonState::ACTIVE) {
|
||||||
|
1.0f32
|
||||||
|
} else {
|
||||||
|
0.0f32
|
||||||
|
};
|
||||||
|
self.mouse_datamap.grab = if sk.input_key(Key::MouseBack).contains(ButtonState::ACTIVE)
|
||||||
|
|| sk
|
||||||
|
.input_key(Key::MouseForward)
|
||||||
|
.contains(ButtonState::ACTIVE)
|
||||||
{
|
{
|
||||||
1.0f32
|
1.0f32
|
||||||
} else {
|
} else {
|
||||||
@@ -104,7 +129,7 @@ impl MousePointer {
|
|||||||
};
|
};
|
||||||
self.mouse_datamap.scroll_continuous = vec2(0.0, mouse.scroll_change / 120.0);
|
self.mouse_datamap.scroll_continuous = vec2(0.0, mouse.scroll_change / 120.0);
|
||||||
self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0);
|
self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0);
|
||||||
*self.pointer.datamap.lock() = self.mouse_datamap.to_datamap().ok();
|
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).ok();
|
||||||
}
|
}
|
||||||
self.send_keyboard_input(sk);
|
self.send_keyboard_input(sk);
|
||||||
}
|
}
|
||||||
@@ -115,7 +140,7 @@ impl MousePointer {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|rx| mask_matches(&rx.mask, &self.keyboard_sender.mask))
|
.filter(|rx| mask_matches(&rx.mask, &self.keyboard_sender.mask))
|
||||||
.map(|rx| {
|
.map(|rx| {
|
||||||
let result = rx.field.ray_march(Ray {
|
let result = rx.field_node.get_aspect::<Field>().unwrap().ray_march(Ray {
|
||||||
origin: vec3(0.0, 0.0, 0.0),
|
origin: vec3(0.0, 0.0, 0.0),
|
||||||
direction: vec3(0.0, 0.0, -1.0),
|
direction: vec3(0.0, 0.0, -1.0),
|
||||||
space: self.spatial.clone(),
|
space: self.spatial.clone(),
|
||||||
@@ -151,8 +176,12 @@ impl MousePointer {
|
|||||||
|
|
||||||
self.keyboard_datamap.keys = keys;
|
self.keyboard_datamap.keys = keys;
|
||||||
if !self.keyboard_datamap.keys.is_empty() {
|
if !self.keyboard_datamap.keys.is_empty() {
|
||||||
rx.send_data(&self.node.uid, self.keyboard_datamap.serialize().unwrap())
|
pulse_receiver_client::data(
|
||||||
.unwrap();
|
&rx.node.upgrade().unwrap(),
|
||||||
|
&self.node.uid,
|
||||||
|
&Datamap::from_typed(&self.keyboard_datamap).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{client::INTERNAL_CLIENT, typed_datamap::TypedDatamap},
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
input::{tip::Tip, InputMethod, InputType},
|
input::{tip::Tip, InputMethod, InputType},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
@@ -9,6 +9,7 @@ use crate::{
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use stardust_xr::values::Datamap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
named_colors::WHITE, ButtonState, Handed, Model, RenderLayer, StereoKitDraw,
|
named_colors::WHITE, ButtonState, Handed, Model, RenderLayer, StereoKitDraw,
|
||||||
@@ -27,11 +28,11 @@ pub struct SkController {
|
|||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
model: Model,
|
model: Model,
|
||||||
handed: Handed,
|
handed: Handed,
|
||||||
datamap: TypedDatamap<ControllerDatamap>,
|
datamap: ControllerDatamap,
|
||||||
}
|
}
|
||||||
impl SkController {
|
impl SkController {
|
||||||
pub fn new(sk: &impl StereoKitMultiThread, handed: Handed) -> Result<Self> {
|
pub fn new(sk: &impl StereoKitMultiThread, handed: Handed) -> Result<Self> {
|
||||||
let _node = Node::create(
|
let _node = Node::create_parent_name(
|
||||||
&INTERNAL_CLIENT,
|
&INTERNAL_CLIENT,
|
||||||
"",
|
"",
|
||||||
if handed == Handed::Left {
|
if handed == Handed::Left {
|
||||||
@@ -42,7 +43,7 @@ impl SkController {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&_node, None, Mat4::IDENTITY, false)?;
|
Spatial::add_to(&_node, None, Mat4::IDENTITY, false);
|
||||||
let model = sk.model_create_mem("cursor.glb", include_bytes!("cursor.glb"), None)?;
|
let model = sk.model_create_mem("cursor.glb", include_bytes!("cursor.glb"), None)?;
|
||||||
let tip = InputType::Tip(Tip::default());
|
let tip = InputType::Tip(Tip::default());
|
||||||
let input = InputMethod::add_to(&_node, tip, None)?;
|
let input = InputMethod::add_to(&_node, tip, None)?;
|
||||||
@@ -73,6 +74,6 @@ impl SkController {
|
|||||||
self.datamap.select = controller.trigger;
|
self.datamap.select = controller.trigger;
|
||||||
self.datamap.grab = controller.grip;
|
self.datamap.grab = controller.grip;
|
||||||
self.datamap.scroll = controller.stick;
|
self.datamap.scroll = controller.stick;
|
||||||
*self.input.datamap.lock() = self.datamap.to_datamap().ok();
|
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{client::INTERNAL_CLIENT, typed_datamap::TypedDatamap},
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
input::{hand::Hand, InputMethod, InputType},
|
input::{hand::Hand, InputMethod, InputType},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
@@ -10,7 +10,10 @@ use color_eyre::eyre::Result;
|
|||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use stardust_xr::schemas::flat::{Hand as FlatHand, Joint};
|
use stardust_xr::{
|
||||||
|
schemas::flat::{Hand as FlatHand, Joint},
|
||||||
|
values::Datamap,
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
|
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
|
||||||
|
|
||||||
@@ -33,12 +36,13 @@ pub struct SkHand {
|
|||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
handed: Handed,
|
handed: Handed,
|
||||||
datamap: TypedDatamap<HandDatamap>,
|
datamap: HandDatamap,
|
||||||
}
|
}
|
||||||
impl SkHand {
|
impl SkHand {
|
||||||
pub fn new(handed: Handed) -> Result<Self> {
|
pub fn new(handed: Handed) -> Result<Self> {
|
||||||
let _node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
let _node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
|
||||||
Spatial::add_to(&_node, None, Mat4::IDENTITY, false)?;
|
.add_to_scenegraph()?;
|
||||||
|
Spatial::add_to(&_node, None, Mat4::IDENTITY, false);
|
||||||
let hand = InputType::Hand(Box::new(Hand {
|
let hand = InputType::Hand(Box::new(Hand {
|
||||||
base: FlatHand {
|
base: FlatHand {
|
||||||
right: handed == Handed::Right,
|
right: handed == Handed::Right,
|
||||||
@@ -98,6 +102,6 @@ impl SkHand {
|
|||||||
}
|
}
|
||||||
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
||||||
self.datamap.grab_strength = sk_hand.grip_activation;
|
self.datamap.grab_strength = sk_hand.grip_activation;
|
||||||
*self.input.datamap.lock() = self.datamap.to_datamap().ok();
|
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ use glam::Mat4;
|
|||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use stardust_xr::values::Datamap;
|
||||||
use stereokit::StereoKitMultiThread;
|
use stereokit::StereoKitMultiThread;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
data::{Mask, PulseReceiver},
|
data::PulseReceiver,
|
||||||
fields::{r#box::BoxField, Field},
|
fields::{r#box::BoxField, Field},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Node,
|
Node,
|
||||||
@@ -39,12 +40,17 @@ pub struct PlaySpace {
|
|||||||
}
|
}
|
||||||
impl PlaySpace {
|
impl PlaySpace {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false)?;
|
.add_to_scenegraph()?;
|
||||||
let field = BoxField::add_to(&node, [0.0; 3].into())?;
|
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
|
||||||
|
BoxField::add_to(&node, [0.0; 3].into());
|
||||||
|
let field = node.get_aspect::<Field>()?.clone();
|
||||||
|
|
||||||
let pulse_rx =
|
let pulse_rx = PulseReceiver::add_to(
|
||||||
PulseReceiver::add_to(&node, field.clone(), Mask::from_struct::<PlaySpaceMap>())?;
|
&node,
|
||||||
|
node.clone(),
|
||||||
|
Datamap::from_typed(PlaySpaceMap::default())?,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(PlaySpace {
|
Ok(PlaySpace {
|
||||||
_node: node,
|
_node: node,
|
||||||
@@ -60,7 +66,9 @@ impl PlaySpace {
|
|||||||
pose.orientation,
|
pose.orientation,
|
||||||
pose.position,
|
pose.position,
|
||||||
));
|
));
|
||||||
let Field::Box(box_field) = self.field.as_ref() else {return};
|
let Field::Box(box_field) = self.field.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
box_field.set_size(
|
box_field.set_size(
|
||||||
[
|
[
|
||||||
sk.world_get_bounds_size().x,
|
sk.world_get_bounds_size().x,
|
||||||
|
|||||||
139
src/wayland/drm.rs
Normal file
139
src/wayland/drm.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
// Re-export only the actual code, and then only use this re-export
|
||||||
|
// The `generated` module below is just some boilerplate to properly isolate stuff
|
||||||
|
// and avoid exposing internal details.
|
||||||
|
//
|
||||||
|
// You can use all the types from my_protocol as if they went from `wayland_client::protocol`.
|
||||||
|
pub use generated::wl_drm;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals, non_camel_case_types)]
|
||||||
|
mod generated {
|
||||||
|
use smithay::reexports::wayland_server::{self, protocol::*};
|
||||||
|
|
||||||
|
pub mod __interfaces {
|
||||||
|
use smithay::reexports::wayland_server::protocol::__interfaces::*;
|
||||||
|
wayland_scanner::generate_interfaces!("src/wayland/wayland-drm.xml");
|
||||||
|
}
|
||||||
|
use self::__interfaces::*;
|
||||||
|
|
||||||
|
wayland_scanner::generate_server_code!("src/wayland/wayland-drm.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
use super::state::WaylandState;
|
||||||
|
use smithay::{
|
||||||
|
backend::allocator::{
|
||||||
|
dmabuf::{Dmabuf, DmabufFlags},
|
||||||
|
Fourcc, Modifier,
|
||||||
|
},
|
||||||
|
reexports::wayland_server::{
|
||||||
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
impl GlobalDispatch<wl_drm::WlDrm, (), WaylandState> for WaylandState {
|
||||||
|
fn bind(
|
||||||
|
state: &mut WaylandState,
|
||||||
|
_dh: &DisplayHandle,
|
||||||
|
_client: &Client,
|
||||||
|
resource: New<wl_drm::WlDrm>,
|
||||||
|
_global_data: &(),
|
||||||
|
data_init: &mut DataInit<'_, WaylandState>,
|
||||||
|
) {
|
||||||
|
let drm_instance = data_init.init(resource, ());
|
||||||
|
|
||||||
|
drm_instance.device("/dev/dri/renderD128".to_string());
|
||||||
|
if drm_instance.version() >= 2 {
|
||||||
|
drm_instance.capabilities(wl_drm::Capability::Prime as u32);
|
||||||
|
}
|
||||||
|
for format in state.drm_formats.iter() {
|
||||||
|
if let Ok(converted) = wl_drm::Format::try_from(*format as u32) {
|
||||||
|
drm_instance.format(converted as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_view(_client: Client, _global_dataa: &()) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<wl_drm::WlDrm, (), WaylandState> for WaylandState {
|
||||||
|
fn request(
|
||||||
|
state: &mut WaylandState,
|
||||||
|
_client: &Client,
|
||||||
|
drm: &wl_drm::WlDrm,
|
||||||
|
request: wl_drm::Request,
|
||||||
|
_data: &(),
|
||||||
|
_dh: &DisplayHandle,
|
||||||
|
data_init: &mut DataInit<'_, WaylandState>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
wl_drm::Request::Authenticate { .. } => drm.authenticated(),
|
||||||
|
wl_drm::Request::CreateBuffer { .. } => drm.post_error(
|
||||||
|
wl_drm::Error::InvalidName,
|
||||||
|
String::from("Flink handles are unsupported, use PRIME"),
|
||||||
|
),
|
||||||
|
wl_drm::Request::CreatePlanarBuffer { .. } => drm.post_error(
|
||||||
|
wl_drm::Error::InvalidName,
|
||||||
|
String::from("Flink handles are unsupported, use PRIME"),
|
||||||
|
),
|
||||||
|
wl_drm::Request::CreatePrimeBuffer {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
offset0,
|
||||||
|
stride0,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let format = match Fourcc::try_from(format) {
|
||||||
|
Ok(format) => {
|
||||||
|
if !state.drm_formats.contains(&format) {
|
||||||
|
drm.post_error(
|
||||||
|
wl_drm::Error::InvalidFormat,
|
||||||
|
String::from("Format not advertised by wl_drm"),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
drm.post_error(
|
||||||
|
wl_drm::Error::InvalidFormat,
|
||||||
|
String::from("Format unknown / not advertised by wl_drm"),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if width < 1 || height < 1 {
|
||||||
|
drm.post_error(
|
||||||
|
wl_drm::Error::InvalidFormat,
|
||||||
|
String::from("width or height not positive"),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dma = Dmabuf::builder((width, height), format, DmabufFlags::empty());
|
||||||
|
dma.add_plane(name, 0, offset0 as u32, stride0 as u32, Modifier::Invalid);
|
||||||
|
match dma.build() {
|
||||||
|
Some(dmabuf) => {
|
||||||
|
state.dmabuf_tx.send((dmabuf.clone(), None)).unwrap();
|
||||||
|
data_init.init(id, dmabuf);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Buffer import failed. The protocol documentation heavily implies killing the
|
||||||
|
// client is the right thing to do here.
|
||||||
|
drm.post_error(
|
||||||
|
wl_drm::Error::InvalidName,
|
||||||
|
"dmabuf global was destroyed on server",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,28 +5,40 @@ mod seat;
|
|||||||
mod state;
|
mod state;
|
||||||
mod surface;
|
mod surface;
|
||||||
// mod xdg_activation;
|
// mod xdg_activation;
|
||||||
|
mod drm;
|
||||||
mod xdg_shell;
|
mod xdg_shell;
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland_rootful")]
|
||||||
pub mod xwayland;
|
pub mod xwayland_rootful;
|
||||||
|
#[cfg(feature = "xwayland_rootful")]
|
||||||
|
use self::xwayland_rootful::X11Lock;
|
||||||
|
#[cfg(feature = "xwayland_rootful")]
|
||||||
|
use crate::wayland::xwayland_rootful::start_xwayland;
|
||||||
|
#[cfg(feature = "xwayland_rootless")]
|
||||||
|
pub mod xwayland_rootless;
|
||||||
|
#[cfg(feature = "xwayland_rootless")]
|
||||||
|
use self::xwayland_rootless::XWaylandState;
|
||||||
|
|
||||||
use self::{state::WaylandState, surface::CORE_SURFACES};
|
use self::{state::WaylandState, surface::CORE_SURFACES};
|
||||||
use crate::core::buffers::BufferManager;
|
|
||||||
use crate::wayland::seat::SeatData;
|
use crate::wayland::seat::SeatData;
|
||||||
use crate::{core::task, wayland::state::ClientState};
|
use crate::{core::task, wayland::state::ClientState};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use global_counter::primitive::exact::CounterU32;
|
use global_counter::primitive::exact::CounterU32;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use sk::StereoKitDraw;
|
use sk::StereoKitDraw;
|
||||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||||
|
use smithay::backend::egl::EGLContext;
|
||||||
|
use smithay::backend::renderer::gles::GlesRenderer;
|
||||||
use smithay::backend::renderer::ImportDma;
|
use smithay::backend::renderer::ImportDma;
|
||||||
use smithay::reexports::wayland_server::backend::ClientId;
|
use smithay::reexports::wayland_server::backend::ClientId;
|
||||||
use smithay::reexports::wayland_server::DisplayHandle;
|
use smithay::reexports::wayland_server::DisplayHandle;
|
||||||
use smithay::reexports::wayland_server::{Display, ListeningSocket};
|
use smithay::reexports::wayland_server::{Display, ListeningSocket};
|
||||||
|
use smithay::wayland::dmabuf;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
use std::os::unix::prelude::AsRawFd;
|
use std::os::unix::prelude::AsRawFd;
|
||||||
use std::{
|
use std::{
|
||||||
|
ffi::c_void,
|
||||||
os::unix::{net::UnixListener, prelude::FromRawFd},
|
os::unix::{net::UnixListener, prelude::FromRawFd},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@@ -37,9 +49,31 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
use tracing::{debug_span, info, instrument};
|
use tracing::{debug_span, info, instrument};
|
||||||
|
|
||||||
|
pub static X_DISPLAY: OnceCell<u32> = OnceCell::new();
|
||||||
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
||||||
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
|
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
|
||||||
|
|
||||||
|
struct EGLRawHandles {
|
||||||
|
display: *const c_void,
|
||||||
|
config: *const c_void,
|
||||||
|
context: *const c_void,
|
||||||
|
}
|
||||||
|
fn get_sk_egl() -> Result<EGLRawHandles> {
|
||||||
|
ensure!(
|
||||||
|
unsafe { sk::sys::backend_graphics_get() }
|
||||||
|
== sk::sys::backend_graphics__backend_graphics_opengles_egl,
|
||||||
|
"StereoKit is not running using EGL!"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(unsafe {
|
||||||
|
EGLRawHandles {
|
||||||
|
display: sk::sys::backend_opengl_egl_get_display() as *const c_void,
|
||||||
|
config: sk::sys::backend_opengl_egl_get_config() as *const c_void,
|
||||||
|
context: sk::sys::backend_opengl_egl_get_context() as *const c_void,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DisplayWrapper(Mutex<Display<WaylandState>>, DisplayHandle);
|
pub struct DisplayWrapper(Mutex<Display<WaylandState>>, DisplayHandle);
|
||||||
impl DisplayWrapper {
|
impl DisplayWrapper {
|
||||||
pub fn handle(&self) -> DisplayHandle {
|
pub fn handle(&self) -> DisplayHandle {
|
||||||
@@ -62,21 +96,34 @@ pub struct Wayland {
|
|||||||
display: Arc<DisplayWrapper>,
|
display: Arc<DisplayWrapper>,
|
||||||
pub socket_name: Option<String>,
|
pub socket_name: Option<String>,
|
||||||
join_handle: JoinHandle<Result<()>>,
|
join_handle: JoinHandle<Result<()>>,
|
||||||
dmabuf_rx: UnboundedReceiver<Dmabuf>,
|
renderer: GlesRenderer,
|
||||||
|
dmabuf_rx: UnboundedReceiver<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||||
wayland_state: Arc<Mutex<WaylandState>>,
|
wayland_state: Arc<Mutex<WaylandState>>,
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland_rootful")]
|
||||||
pub xwayland_state: xwayland::XWaylandState,
|
pub x_lock: X11Lock,
|
||||||
|
#[cfg(feature = "xwayland_rootless")]
|
||||||
|
pub xwayland_state: XWaylandState,
|
||||||
}
|
}
|
||||||
impl Wayland {
|
impl Wayland {
|
||||||
pub fn new(buffer_manager: &BufferManager) -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
|
let egl_raw_handles = get_sk_egl()?;
|
||||||
|
let renderer = unsafe {
|
||||||
|
GlesRenderer::new(EGLContext::from_raw(
|
||||||
|
egl_raw_handles.display,
|
||||||
|
egl_raw_handles.config,
|
||||||
|
egl_raw_handles.context,
|
||||||
|
)?)?
|
||||||
|
};
|
||||||
|
|
||||||
let display: Display<WaylandState> = Display::new()?;
|
let display: Display<WaylandState> = Display::new()?;
|
||||||
let display_handle = display.handle();
|
let display_handle = display.handle();
|
||||||
|
|
||||||
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
||||||
let display = Arc::new(DisplayWrapper(Mutex::new(display), display_handle.clone()));
|
let display = Arc::new(DisplayWrapper(Mutex::new(display), display_handle.clone()));
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
let xwayland_state = xwayland::XWaylandState::create(&display_handle)?;
|
#[cfg(feature = "xwayland_rootless")]
|
||||||
let wayland_state = WaylandState::new(display_handle, &buffer_manager.renderer, dmabuf_tx);
|
let xwayland_state = XWaylandState::create(&display_handle)?;
|
||||||
|
let wayland_state = WaylandState::new(display_handle, &renderer, dmabuf_tx);
|
||||||
|
|
||||||
let socket = ListeningSocket::bind_auto("wayland", 0..33)?;
|
let socket = ListeningSocket::bind_auto("wayland", 0..33)?;
|
||||||
let socket_name = socket
|
let socket_name = socket
|
||||||
@@ -86,6 +133,8 @@ impl Wayland {
|
|||||||
if let Some(socket_name) = &socket_name {
|
if let Some(socket_name) = &socket_name {
|
||||||
let _ = WAYLAND_DISPLAY.set(socket_name.clone());
|
let _ = WAYLAND_DISPLAY.set(socket_name.clone());
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "xwayland_rootful")]
|
||||||
|
let x_display = start_xwayland(socket.as_raw_fd())?;
|
||||||
info!(socket_name, "Wayland active");
|
info!(socket_name, "Wayland active");
|
||||||
|
|
||||||
let join_handle = Wayland::start_loop(display.clone(), socket, wayland_state.clone())?;
|
let join_handle = Wayland::start_loop(display.clone(), socket, wayland_state.clone())?;
|
||||||
@@ -94,9 +143,12 @@ impl Wayland {
|
|||||||
display,
|
display,
|
||||||
socket_name,
|
socket_name,
|
||||||
join_handle,
|
join_handle,
|
||||||
|
renderer,
|
||||||
dmabuf_rx,
|
dmabuf_rx,
|
||||||
wayland_state,
|
wayland_state,
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland_rootful")]
|
||||||
|
x_lock: x_display,
|
||||||
|
#[cfg(feature = "xwayland_rootless")]
|
||||||
xwayland_state,
|
xwayland_state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -144,17 +196,17 @@ impl Wayland {
|
|||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(
|
#[instrument(level = "debug", name = "Wayland frame", skip(self, sk))]
|
||||||
level = "debug",
|
pub fn update(&mut self, sk: &impl StereoKitDraw) {
|
||||||
name = "Wayland frame",
|
while let Ok((dmabuf, notifier)) = self.dmabuf_rx.try_recv() {
|
||||||
skip(self, sk, buffer_manager)
|
if self.renderer.import_dmabuf(&dmabuf, None).is_err() {
|
||||||
)]
|
if let Some(notifier) = notifier {
|
||||||
pub fn update(&mut self, sk: &impl StereoKitDraw, buffer_manager: &mut BufferManager) {
|
notifier.failed();
|
||||||
while let Ok(dmabuf) = self.dmabuf_rx.try_recv() {
|
}
|
||||||
let _ = buffer_manager.renderer.import_dmabuf(&dmabuf, None);
|
}
|
||||||
}
|
}
|
||||||
for core_surface in CORE_SURFACES.get_valid_contents() {
|
for core_surface in CORE_SURFACES.get_valid_contents() {
|
||||||
core_surface.process(sk, &mut buffer_manager.renderer);
|
core_surface.process(sk, &mut self.renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.display.flush_clients(None);
|
self.display.flush_clients(None);
|
||||||
@@ -167,6 +219,12 @@ impl Wayland {
|
|||||||
core_surface.frame(sk, output.clone());
|
core_surface.frame(sk, output.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_context_current(&self) {
|
||||||
|
unsafe {
|
||||||
|
let _ = self.renderer.egl_context().make_current();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Wayland {
|
impl Drop for Wayland {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
use crate::wayland::seat::SeatData;
|
use super::DisplayWrapper;
|
||||||
|
use crate::wayland::{drm::wl_drm::WlDrm, seat::SeatData};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{allocator::dmabuf::Dmabuf, egl::EGLDevice, renderer::gles::GlesRenderer},
|
backend::{
|
||||||
|
allocator::{dmabuf::Dmabuf, Fourcc},
|
||||||
|
egl::EGLDevice,
|
||||||
|
renderer::gles::GlesRenderer,
|
||||||
|
},
|
||||||
delegate_dmabuf, delegate_output, delegate_shm,
|
delegate_dmabuf, delegate_output, delegate_shm,
|
||||||
output::{Mode, Output, Scale, Subpixel},
|
output::{Mode, Output, Scale, Subpixel},
|
||||||
reexports::{
|
reexports::{
|
||||||
@@ -23,7 +28,6 @@ use smithay::{
|
|||||||
compositor::{CompositorClientState, CompositorState},
|
compositor::{CompositorClientState, CompositorState},
|
||||||
dmabuf::{
|
dmabuf::{
|
||||||
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||||
ImportError,
|
|
||||||
},
|
},
|
||||||
shell::kde::decoration::KdeDecorationState,
|
shell::kde::decoration::KdeDecorationState,
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
@@ -33,8 +37,6 @@ use std::sync::{Arc, Weak};
|
|||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use super::DisplayWrapper;
|
|
||||||
|
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub id: OnceCell<ClientId>,
|
pub id: OnceCell<ClientId>,
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
@@ -43,7 +45,9 @@ pub struct ClientState {
|
|||||||
}
|
}
|
||||||
impl ClientState {
|
impl ClientState {
|
||||||
pub fn flush(&self) {
|
pub fn flush(&self) {
|
||||||
let Some(display) = self.display.upgrade() else {return};
|
let Some(display) = self.display.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let _ = display.flush_clients(self.id.get().cloned());
|
let _ = display.flush_clients(self.id.get().cloned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +74,8 @@ pub struct WaylandState {
|
|||||||
pub kde_decoration_state: KdeDecorationState,
|
pub kde_decoration_state: KdeDecorationState,
|
||||||
pub shm_state: ShmState,
|
pub shm_state: ShmState,
|
||||||
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
||||||
dmabuf_tx: UnboundedSender<Dmabuf>,
|
pub drm_formats: Vec<Fourcc>,
|
||||||
|
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +83,7 @@ impl WaylandState {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
display_handle: DisplayHandle,
|
display_handle: DisplayHandle,
|
||||||
renderer: &GlesRenderer,
|
renderer: &GlesRenderer,
|
||||||
dmabuf_tx: UnboundedSender<Dmabuf>,
|
dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||||
) -> Arc<Mutex<Self>> {
|
) -> Arc<Mutex<Self>> {
|
||||||
let compositor_state = CompositorState::new::<Self>(&display_handle);
|
let compositor_state = CompositorState::new::<Self>(&display_handle);
|
||||||
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle);
|
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle);
|
||||||
@@ -87,13 +92,14 @@ impl WaylandState {
|
|||||||
let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
|
let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
|
||||||
let render_node = EGLDevice::device_for_display(renderer.egl_context().display())
|
let render_node = EGLDevice::device_for_display(renderer.egl_context().display())
|
||||||
.and_then(|device| device.try_get_render_node());
|
.and_then(|device| device.try_get_render_node());
|
||||||
|
|
||||||
let dmabuf_formats = renderer
|
let dmabuf_formats = renderer
|
||||||
.egl_context()
|
.egl_context()
|
||||||
.dmabuf_render_formats()
|
.dmabuf_render_formats()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let drm_formats = dmabuf_formats.iter().map(|f| f.code).collect();
|
||||||
|
|
||||||
let dmabuf_default_feedback = match render_node {
|
let dmabuf_default_feedback = match render_node {
|
||||||
Ok(Some(node)) => DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats.clone())
|
Ok(Some(node)) => DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats.clone())
|
||||||
.build()
|
.build()
|
||||||
@@ -147,6 +153,7 @@ impl WaylandState {
|
|||||||
display_handle.create_global::<Self, WlDataDeviceManager, _>(3, ());
|
display_handle.create_global::<Self, WlDataDeviceManager, _>(3, ());
|
||||||
display_handle.create_global::<Self, XdgWmBase, _>(5, ());
|
display_handle.create_global::<Self, XdgWmBase, _>(5, ());
|
||||||
display_handle.create_global::<Self, ZxdgDecorationManagerV1, _>(1, ());
|
display_handle.create_global::<Self, ZxdgDecorationManagerV1, _>(1, ());
|
||||||
|
display_handle.create_global::<Self, WlDrm, _>(2, ());
|
||||||
|
|
||||||
info!("Init Wayland compositor");
|
info!("Init Wayland compositor");
|
||||||
|
|
||||||
@@ -159,6 +166,7 @@ impl WaylandState {
|
|||||||
// xdg_activation_state,
|
// xdg_activation_state,
|
||||||
kde_decoration_state,
|
kde_decoration_state,
|
||||||
shm_state,
|
shm_state,
|
||||||
|
drm_formats,
|
||||||
dmabuf_state,
|
dmabuf_state,
|
||||||
dmabuf_tx,
|
dmabuf_tx,
|
||||||
output,
|
output,
|
||||||
@@ -188,8 +196,9 @@ impl DmabufHandler for WaylandState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
_global: &DmabufGlobal,
|
_global: &DmabufGlobal,
|
||||||
dmabuf: Dmabuf,
|
dmabuf: Dmabuf,
|
||||||
) -> Result<(), dmabuf::ImportError> {
|
notifier: dmabuf::ImportNotifier,
|
||||||
self.dmabuf_tx.send(dmabuf).map_err(|_| ImportError::Failed)
|
) {
|
||||||
|
self.dmabuf_tx.send((dmabuf, Some(notifier))).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate_dmabuf!(WaylandState);
|
delegate_dmabuf!(WaylandState);
|
||||||
|
|||||||
189
src/wayland/wayland-drm.xml
Normal file
189
src/wayland/wayland-drm.xml
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="drm">
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2008-2011 Kristian Høgsberg
|
||||||
|
Copyright © 2010-2011 Intel Corporation
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that\n the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<!-- drm support. This object is created by the server and published
|
||||||
|
using the display's global event. -->
|
||||||
|
<interface name="wl_drm" version="2">
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="authenticate_fail" value="0" />
|
||||||
|
<entry name="invalid_format" value="1" />
|
||||||
|
<entry name="invalid_name" value="2" />
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="format">
|
||||||
|
<!-- The drm format codes match the #defines in drm_fourcc.h.
|
||||||
|
The formats actually supported by the compositor will be
|
||||||
|
reported by the format event. New codes must not be added,
|
||||||
|
unless directly taken from drm_fourcc.h. -->
|
||||||
|
<entry name="c8" value="0x20203843" />
|
||||||
|
<entry name="rgb332" value="0x38424752" />
|
||||||
|
<entry name="bgr233" value="0x38524742" />
|
||||||
|
<entry name="xrgb4444" value="0x32315258" />
|
||||||
|
<entry name="xbgr4444" value="0x32314258" />
|
||||||
|
<entry name="rgbx4444" value="0x32315852" />
|
||||||
|
<entry name="bgrx4444" value="0x32315842" />
|
||||||
|
<entry name="argb4444" value="0x32315241" />
|
||||||
|
<entry name="abgr4444" value="0x32314241" />
|
||||||
|
<entry name="rgba4444" value="0x32314152" />
|
||||||
|
<entry name="bgra4444" value="0x32314142" />
|
||||||
|
<entry name="xrgb1555" value="0x35315258" />
|
||||||
|
<entry name="xbgr1555" value="0x35314258" />
|
||||||
|
<entry name="rgbx5551" value="0x35315852" />
|
||||||
|
<entry name="bgrx5551" value="0x35315842" />
|
||||||
|
<entry name="argb1555" value="0x35315241" />
|
||||||
|
<entry name="abgr1555" value="0x35314241" />
|
||||||
|
<entry name="rgba5551" value="0x35314152" />
|
||||||
|
<entry name="bgra5551" value="0x35314142" />
|
||||||
|
<entry name="rgb565" value="0x36314752" />
|
||||||
|
<entry name="bgr565" value="0x36314742" />
|
||||||
|
<entry name="rgb888" value="0x34324752" />
|
||||||
|
<entry name="bgr888" value="0x34324742" />
|
||||||
|
<entry name="xrgb8888" value="0x34325258" />
|
||||||
|
<entry name="xbgr8888" value="0x34324258" />
|
||||||
|
<entry name="rgbx8888" value="0x34325852" />
|
||||||
|
<entry name="bgrx8888" value="0x34325842" />
|
||||||
|
<entry name="argb8888" value="0x34325241" />
|
||||||
|
<entry name="abgr8888" value="0x34324241" />
|
||||||
|
<entry name="rgba8888" value="0x34324152" />
|
||||||
|
<entry name="bgra8888" value="0x34324142" />
|
||||||
|
<entry name="xrgb2101010" value="0x30335258" />
|
||||||
|
<entry name="xbgr2101010" value="0x30334258" />
|
||||||
|
<entry name="rgbx1010102" value="0x30335852" />
|
||||||
|
<entry name="bgrx1010102" value="0x30335842" />
|
||||||
|
<entry name="argb2101010" value="0x30335241" />
|
||||||
|
<entry name="abgr2101010" value="0x30334241" />
|
||||||
|
<entry name="rgba1010102" value="0x30334152" />
|
||||||
|
<entry name="bgra1010102" value="0x30334142" />
|
||||||
|
<entry name="yuyv" value="0x56595559" />
|
||||||
|
<entry name="yvyu" value="0x55595659" />
|
||||||
|
<entry name="uyvy" value="0x59565955" />
|
||||||
|
<entry name="vyuy" value="0x59555956" />
|
||||||
|
<entry name="ayuv" value="0x56555941" />
|
||||||
|
<entry name="xyuv8888" value="0x56555958" />
|
||||||
|
<entry name="nv12" value="0x3231564e" />
|
||||||
|
<entry name="nv21" value="0x3132564e" />
|
||||||
|
<entry name="nv16" value="0x3631564e" />
|
||||||
|
<entry name="nv61" value="0x3136564e" />
|
||||||
|
<entry name="yuv410" value="0x39565559" />
|
||||||
|
<entry name="yvu410" value="0x39555659" />
|
||||||
|
<entry name="yuv411" value="0x31315559" />
|
||||||
|
<entry name="yvu411" value="0x31315659" />
|
||||||
|
<entry name="yuv420" value="0x32315559" />
|
||||||
|
<entry name="yvu420" value="0x32315659" />
|
||||||
|
<entry name="yuv422" value="0x36315559" />
|
||||||
|
<entry name="yvu422" value="0x36315659" />
|
||||||
|
<entry name="yuv444" value="0x34325559" />
|
||||||
|
<entry name="yvu444" value="0x34325659" />
|
||||||
|
<entry name="abgr16f" value="0x48344241" />
|
||||||
|
<entry name="xbgr16f" value="0x48344258" />
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<!-- Call this request with the magic received from drmGetMagic().
|
||||||
|
It will be passed on to the drmAuthMagic() or
|
||||||
|
DRIAuthConnection() call. This authentication must be
|
||||||
|
completed before create_buffer could be used. -->
|
||||||
|
<request name="authenticate">
|
||||||
|
<arg name="id" type="uint" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Create a wayland buffer for the named DRM buffer. The DRM
|
||||||
|
surface must have a name using the flink ioctl -->
|
||||||
|
<request name="create_buffer">
|
||||||
|
<arg name="id" type="new_id" interface="wl_buffer" />
|
||||||
|
<arg name="name" type="uint" />
|
||||||
|
<arg name="width" type="int" />
|
||||||
|
<arg name="height" type="int" />
|
||||||
|
<arg name="stride" type="uint" />
|
||||||
|
<arg name="format" type="uint" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Create a wayland buffer for the named DRM buffer. The DRM
|
||||||
|
surface must have a name using the flink ioctl -->
|
||||||
|
<request name="create_planar_buffer">
|
||||||
|
<arg name="id" type="new_id" interface="wl_buffer" />
|
||||||
|
<arg name="name" type="uint" />
|
||||||
|
<arg name="width" type="int" />
|
||||||
|
<arg name="height" type="int" />
|
||||||
|
<arg name="format" type="uint" />
|
||||||
|
<arg name="offset0" type="int" />
|
||||||
|
<arg name="stride0" type="int" />
|
||||||
|
<arg name="offset1" type="int" />
|
||||||
|
<arg name="stride1" type="int" />
|
||||||
|
<arg name="offset2" type="int" />
|
||||||
|
<arg name="stride2" type="int" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Notification of the path of the drm device which is used by
|
||||||
|
the server. The client should use this device for creating
|
||||||
|
local buffers. Only buffers created from this device should
|
||||||
|
be be passed to the server using this drm object's
|
||||||
|
create_buffer request. -->
|
||||||
|
<event name="device">
|
||||||
|
<arg name="name" type="string" />
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="format">
|
||||||
|
<arg name="format" type="uint" />
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<!-- Raised if the authenticate request succeeded -->
|
||||||
|
<event name="authenticated" />
|
||||||
|
|
||||||
|
<enum name="capability" since="2">
|
||||||
|
<description summary="wl_drm capability bitmask">
|
||||||
|
Bitmask of capabilities.
|
||||||
|
</description>
|
||||||
|
<entry name="prime" value="1" summary="wl_drm prime available" />
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="capabilities">
|
||||||
|
<arg name="value" type="uint" />
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<!-- Create a wayland buffer for the prime fd. Use for regular and planar
|
||||||
|
buffers. Pass 0 for offset and stride for unused planes. -->
|
||||||
|
<request name="create_prime_buffer" since="2">
|
||||||
|
<arg name="id" type="new_id" interface="wl_buffer" />
|
||||||
|
<arg name="name" type="fd" />
|
||||||
|
<arg name="width" type="int" />
|
||||||
|
<arg name="height" type="int" />
|
||||||
|
<arg name="format" type="uint" />
|
||||||
|
<arg name="offset0" type="int" />
|
||||||
|
<arg name="stride0" type="int" />
|
||||||
|
<arg name="offset1" type="int" />
|
||||||
|
<arg name="stride1" type="int" />
|
||||||
|
<arg name="offset2" type="int" />
|
||||||
|
<arg name="stride2" type="int" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</protocol>
|
||||||
267
src/wayland/xwayland_rootful.rs
Normal file
267
src/wayland/xwayland_rootful.rs
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
use crate::core::{client::get_env, task};
|
||||||
|
use crate::STOP_NOTIFIER;
|
||||||
|
use smithay::reexports::rustix;
|
||||||
|
use smithay::reexports::rustix::io::{fcntl_setfd, Errno, FdFlags};
|
||||||
|
use smithay::reexports::rustix::net::SocketAddrUnix;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::{
|
||||||
|
io::ErrorKind,
|
||||||
|
os::{
|
||||||
|
fd::{AsRawFd, BorrowedFd, RawFd},
|
||||||
|
unix::process::CommandExt,
|
||||||
|
},
|
||||||
|
process::{ChildStdout, Command, Stdio},
|
||||||
|
};
|
||||||
|
use tokio::net::{UnixListener, UnixStream};
|
||||||
|
use tokio::task::AbortHandle;
|
||||||
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
|
use super::X_DISPLAY;
|
||||||
|
|
||||||
|
pub fn start_xwayland(wayland_socket: RawFd) -> std::io::Result<X11Lock> {
|
||||||
|
let (mut lock, listener) = bind_socket()?;
|
||||||
|
|
||||||
|
let abort_handle = task::new(|| "X11 Client Acceptor", async move {
|
||||||
|
loop {
|
||||||
|
let Ok((stream, _)) = tokio::select! {
|
||||||
|
_ = STOP_NOTIFIER.notified() => break,
|
||||||
|
e = listener.accept() => e,
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok((x_wm_x11, _x_wm_me)) = UnixStream::pair() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(env) = stream
|
||||||
|
.peer_cred()
|
||||||
|
.and_then(|c| c.pid().ok_or(ErrorKind::Other.into()))
|
||||||
|
.and_then(get_env)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = spawn_xwayland(
|
||||||
|
lock.display,
|
||||||
|
wayland_socket,
|
||||||
|
x_wm_x11,
|
||||||
|
stream.as_raw_fd(),
|
||||||
|
env.get("STARDUST_STARTUP_TOKEN").cloned(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|_| ErrorKind::Other)?
|
||||||
|
.abort_handle();
|
||||||
|
lock.x_abort_handle.replace(abort_handle);
|
||||||
|
let _ = X_DISPLAY.set(lock.display);
|
||||||
|
Ok(lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a free X11 display slot and setup
|
||||||
|
pub(crate) fn bind_socket() -> Result<(X11Lock, UnixListener), std::io::Error> {
|
||||||
|
for d in 0..33 {
|
||||||
|
// if fails, try the next one
|
||||||
|
if let Ok(lock) = X11Lock::grab(d) {
|
||||||
|
// we got a lockfile, try and create the socket
|
||||||
|
match open_x11_socket_for_display(d) {
|
||||||
|
Ok(socket) => return Ok((lock, socket)),
|
||||||
|
Err(err) => warn!(display = d, "Failed to create sockets: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we reach here, all values from 0 to 32 failed
|
||||||
|
// we need to stop trying at some point
|
||||||
|
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::AddrInUse,
|
||||||
|
"Could not find a free socket for the XServer.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct X11Lock {
|
||||||
|
display: u32,
|
||||||
|
x_abort_handle: Option<AbortHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl X11Lock {
|
||||||
|
/// Try to grab a lockfile for given X display number
|
||||||
|
fn grab(number: u32) -> Result<X11Lock, ()> {
|
||||||
|
debug!(display = number, "Attempting to aquire an X11 display lock");
|
||||||
|
let filename = format!("/tmp/.X{}-lock", number);
|
||||||
|
let lockfile = ::std::fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&filename);
|
||||||
|
match lockfile {
|
||||||
|
Ok(mut file) => {
|
||||||
|
// we got it, write our PID in it and we're good
|
||||||
|
let ret = file.write_fmt(format_args!(
|
||||||
|
"{:>10}\n",
|
||||||
|
rustix::process::Pid::as_raw(Some(rustix::process::getpid()))
|
||||||
|
));
|
||||||
|
if ret.is_err() {
|
||||||
|
// write to the file failed ? we abandon
|
||||||
|
::std::mem::drop(file);
|
||||||
|
let _ = ::std::fs::remove_file(&filename);
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
debug!(display = number, "X11 lock acquired");
|
||||||
|
// we got the lockfile and wrote our pid to it, all is good
|
||||||
|
Ok(X11Lock {
|
||||||
|
display: number,
|
||||||
|
x_abort_handle: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
debug!(display = number, "Failed to acquire lock");
|
||||||
|
// we could not open the file, now we try to read it
|
||||||
|
// and if it contains the pid of a process that no longer
|
||||||
|
// exist (so if a previous x server claimed it and did not
|
||||||
|
// exit gracefully and remove it), we claim it
|
||||||
|
// if we can't open it, give up
|
||||||
|
let mut file = ::std::fs::File::open(&filename).map_err(|_| ())?;
|
||||||
|
let mut spid = [0u8; 11];
|
||||||
|
file.read_exact(&mut spid).map_err(|_| ())?;
|
||||||
|
::std::mem::drop(file);
|
||||||
|
let pid = rustix::process::Pid::from_raw(
|
||||||
|
::std::str::from_utf8(&spid)
|
||||||
|
.map_err(|_| ())?
|
||||||
|
.trim()
|
||||||
|
.parse::<i32>()
|
||||||
|
.map_err(|_| ())?,
|
||||||
|
)
|
||||||
|
.ok_or(())?;
|
||||||
|
if let Err(Errno::SRCH) = rustix::process::test_kill_process(pid) {
|
||||||
|
// no process whose pid equals the contents of the lockfile exists
|
||||||
|
// remove the lockfile and try grabbing it again
|
||||||
|
if let Ok(()) = ::std::fs::remove_file(filename) {
|
||||||
|
debug!(
|
||||||
|
display = number,
|
||||||
|
"Lock was blocked by a defunct X11 server, trying again"
|
||||||
|
);
|
||||||
|
return X11Lock::grab(number);
|
||||||
|
} else {
|
||||||
|
// we could not remove the lockfile, abort
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we reach here, this lockfile exists and is probably in use, give up
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn display(&self) -> u32 {
|
||||||
|
self.display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for X11Lock {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
info!("Cleaning up X11 lock.");
|
||||||
|
// Cleanup all the X11 files
|
||||||
|
if let Err(e) = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", self.display)) {
|
||||||
|
warn!(error = ?e, "Failed to remove X11 socket");
|
||||||
|
}
|
||||||
|
if let Err(e) = ::std::fs::remove_file(format!("/tmp/.X{}-lock", self.display)) {
|
||||||
|
warn!(error = ?e, "Failed to remove X11 lockfile");
|
||||||
|
}
|
||||||
|
if let Some(join_handle) = self.x_abort_handle.take() {
|
||||||
|
join_handle.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open the two unix sockets an X server listens on
|
||||||
|
///
|
||||||
|
/// Should only be done after the associated lockfile is acquired!
|
||||||
|
fn open_x11_socket_for_display(display: u32) -> rustix::io::Result<UnixListener> {
|
||||||
|
let path = format!("/tmp/.X11-unix/X{}", display);
|
||||||
|
let _ = ::std::fs::remove_file(&path);
|
||||||
|
// We know this path is not too long, these unwrap cannot fail
|
||||||
|
let fs_addr = SocketAddrUnix::new(path.as_bytes()).unwrap();
|
||||||
|
open_socket(fs_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open an unix socket for listening and bind it to given path
|
||||||
|
fn open_socket(addr: SocketAddrUnix) -> rustix::io::Result<UnixListener> {
|
||||||
|
// create an unix stream socket
|
||||||
|
let fd = rustix::net::socket_with(
|
||||||
|
rustix::net::AddressFamily::UNIX,
|
||||||
|
rustix::net::SocketType::STREAM,
|
||||||
|
rustix::net::SocketFlags::CLOEXEC,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
// bind it to requested address
|
||||||
|
rustix::net::bind_unix(&fd, &addr)?;
|
||||||
|
rustix::net::listen(&fd, 1)?;
|
||||||
|
Ok(UnixListener::from_std(std::os::unix::net::UnixListener::from(fd)).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_xwayland(
|
||||||
|
display: u32,
|
||||||
|
wayland_socket: RawFd,
|
||||||
|
wm_socket: UnixStream,
|
||||||
|
listen_socket: RawFd,
|
||||||
|
stardust_startup_token: Option<String>,
|
||||||
|
) -> std::io::Result<ChildStdout> {
|
||||||
|
let mut command = Command::new("sh");
|
||||||
|
|
||||||
|
// We use output stream to communicate because FD is easier to handle than exit code.
|
||||||
|
command.stdout(Stdio::piped());
|
||||||
|
|
||||||
|
let mut xwayland_args = format!(":{} -geometry 1920x1080", display);
|
||||||
|
xwayland_args.push_str(&format!(" -listenfd {}", listen_socket));
|
||||||
|
|
||||||
|
// This command let sh to:
|
||||||
|
// * Set up signal handler for USR1
|
||||||
|
// * Launch Xwayland with USR1 ignored so Xwayland will signal us when it is ready (also redirect
|
||||||
|
// Xwayland's STDOUT to STDERR so its output, if any, won't distract us)
|
||||||
|
// * Print "S" and exit if USR1 is received
|
||||||
|
command.arg("-c").arg(format!(
|
||||||
|
"trap 'echo S' USR1; (trap '' USR1; exec Xwayland {}) 1>&2 & wait",
|
||||||
|
xwayland_args
|
||||||
|
));
|
||||||
|
|
||||||
|
// Setup the environment: clear everything except PATH and XDG_RUNTIME_DIR
|
||||||
|
command.env_clear();
|
||||||
|
for (key, value) in std::env::vars_os() {
|
||||||
|
if key.to_str() == Some("PATH") || key.to_str() == Some("XDG_RUNTIME_DIR") {
|
||||||
|
command.env(key, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.env("WAYLAND_SOCKET", format!("{}", wayland_socket.as_raw_fd()));
|
||||||
|
command.env(
|
||||||
|
"STARDUST_STARTUP_TOKEN",
|
||||||
|
stardust_startup_token.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let wayland_socket_fd = wayland_socket.as_raw_fd();
|
||||||
|
let wm_socket_fd = wm_socket.as_raw_fd();
|
||||||
|
command.pre_exec(move || {
|
||||||
|
// unset the CLOEXEC flag from the sockets we need to pass
|
||||||
|
// to xwayland
|
||||||
|
unset_cloexec(wayland_socket_fd)?;
|
||||||
|
unset_cloexec(wm_socket_fd)?;
|
||||||
|
unset_cloexec(listen_socket)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut child = command.spawn()?;
|
||||||
|
Ok(child.stdout.take().expect("stdout should be piped"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the `O_CLOEXEC` flag from this `Fd`
|
||||||
|
///
|
||||||
|
/// This means that the `Fd` will *not* be automatically
|
||||||
|
/// closed when we `exec()` into XWayland
|
||||||
|
unsafe fn unset_cloexec(fd: RawFd) -> std::io::Result<()> {
|
||||||
|
let fd = BorrowedFd::borrow_raw(fd);
|
||||||
|
fcntl_setfd(fd, FdFlags::empty())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
seat::{KeyboardEvent, PointerEvent, SeatData},
|
seat::{KeyboardEvent, PointerEvent, SeatData},
|
||||||
state::ClientState,
|
X_DISPLAY,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
nodes::{
|
nodes::{
|
||||||
@@ -32,8 +32,6 @@ use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration};
|
|||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
pub static DISPLAY: OnceCell<String> = OnceCell::new();
|
|
||||||
|
|
||||||
pub struct XWaylandState {
|
pub struct XWaylandState {
|
||||||
pub display: u32,
|
pub display: u32,
|
||||||
event_loop_signal: LoopSignal,
|
event_loop_signal: LoopSignal,
|
||||||
@@ -59,10 +57,14 @@ impl XWaylandState {
|
|||||||
client_fd: _,
|
client_fd: _,
|
||||||
display: _,
|
display: _,
|
||||||
} => {
|
} => {
|
||||||
handler.seat = client.get_data::<ClientState>().map(|s| s.seat.clone());
|
handler.seat.client.set(client.id()).unwrap();
|
||||||
handler.wm =
|
handler
|
||||||
X11Wm::start_wm(handle.clone(), dh.clone(), connection, client)
|
.wm
|
||||||
.ok();
|
.set(
|
||||||
|
X11Wm::start_wm(handle.clone(), dh.clone(), connection, client)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
XWaylandEvent::Exited => (),
|
XWaylandEvent::Exited => (),
|
||||||
}
|
}
|
||||||
@@ -81,15 +83,15 @@ impl XWaylandState {
|
|||||||
event_loop_signal: event_loop.get_signal(),
|
event_loop_signal: event_loop.get_signal(),
|
||||||
});
|
});
|
||||||
let mut handler = XWaylandHandler {
|
let mut handler = XWaylandHandler {
|
||||||
|
wm: OnceCell::new(),
|
||||||
|
seat: SeatData::new(&dh),
|
||||||
wayland_display_handle: dh,
|
wayland_display_handle: dh,
|
||||||
wm: None,
|
|
||||||
seat: None,
|
|
||||||
};
|
};
|
||||||
event_loop.run(Duration::from_millis(100), &mut handler, |_| ())
|
event_loop.run(Duration::from_millis(100), &mut handler, |_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = rx.blocking_recv()?;
|
let state = rx.blocking_recv()?;
|
||||||
let _ = DISPLAY.set(format!(":{}", state.display));
|
let _ = X_DISPLAY.set(state.display);
|
||||||
|
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
@@ -102,8 +104,8 @@ impl Drop for XWaylandState {
|
|||||||
|
|
||||||
struct XWaylandHandler {
|
struct XWaylandHandler {
|
||||||
wayland_display_handle: DisplayHandle,
|
wayland_display_handle: DisplayHandle,
|
||||||
wm: Option<X11Wm>,
|
wm: OnceCell<X11Wm>,
|
||||||
seat: Option<Arc<SeatData>>,
|
seat: Arc<SeatData>,
|
||||||
}
|
}
|
||||||
impl XWaylandHandler {
|
impl XWaylandHandler {
|
||||||
fn panel_item(&self, window: &X11Surface) -> Option<Arc<PanelItem<X11Backend>>> {
|
fn panel_item(&self, window: &X11Surface) -> Option<Arc<PanelItem<X11Backend>>> {
|
||||||
@@ -115,7 +117,7 @@ impl XWaylandHandler {
|
|||||||
|
|
||||||
impl XwmHandler for XWaylandHandler {
|
impl XwmHandler for XWaylandHandler {
|
||||||
fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm {
|
fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm {
|
||||||
self.wm.as_mut().unwrap()
|
self.wm.get_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn new_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
@@ -136,14 +138,16 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
let _ = window.set_maximized(true);
|
let _ = window.set_maximized(true);
|
||||||
|
|
||||||
let dh = self.wayland_display_handle.clone();
|
let dh = self.wayland_display_handle.clone();
|
||||||
let seat = self.seat.clone().unwrap();
|
let seat = self.seat.clone();
|
||||||
CoreSurface::add_to(
|
CoreSurface::add_to(
|
||||||
self.wayland_display_handle.clone(),
|
self.wayland_display_handle.clone(),
|
||||||
&window.wl_surface().unwrap(),
|
&window.wl_surface().unwrap(),
|
||||||
{
|
{
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
move || {
|
move || {
|
||||||
let Some(wl_surface) = window.wl_surface() else {return};
|
let Some(wl_surface) = window.wl_surface() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let seat = seat.clone();
|
let seat = seat.clone();
|
||||||
window.user_data().insert_if_missing_threadsafe(|| {
|
window.user_data().insert_if_missing_threadsafe(|| {
|
||||||
let panel_item = PanelItem::create(
|
let panel_item = PanelItem::create(
|
||||||
@@ -164,7 +168,10 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
move |_| {
|
move |_| {
|
||||||
let Some(panel_item) = window.user_data().get::<Arc<PanelItem<X11Backend>>>() else {return};
|
let Some(panel_item) = window.user_data().get::<Arc<PanelItem<X11Backend>>>()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
panel_item.toplevel_size_changed(
|
panel_item.toplevel_size_changed(
|
||||||
[
|
[
|
||||||
window.geometry().size.w as u32,
|
window.geometry().size.w as u32,
|
||||||
@@ -181,6 +188,9 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
|
|
||||||
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
debug!(?window, "Unmap X window");
|
debug!(?window, "Unmap X window");
|
||||||
|
if let Some(panel_item) = window.user_data().get::<Arc<PanelItem<X11Backend>>>() {
|
||||||
|
panel_item.drop_toplevel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
debug!(?window, "Destroy X window");
|
debug!(?window, "Destroy X window");
|
||||||
@@ -210,7 +220,9 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) {
|
fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) {
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
let Some(panel_item) = self.panel_item(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
debug!(?window, button, "X window requests move");
|
debug!(?window, button, "X window requests move");
|
||||||
panel_item.toplevel_move_request();
|
panel_item.toplevel_move_request();
|
||||||
}
|
}
|
||||||
@@ -221,7 +233,9 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
button: u32,
|
button: u32,
|
||||||
resize_edge: ResizeEdge,
|
resize_edge: ResizeEdge,
|
||||||
) {
|
) {
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
let Some(panel_item) = self.panel_item(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
debug!(?window, button, ?resize_edge, "X window requests resize");
|
debug!(?window, button, ?resize_edge, "X window requests resize");
|
||||||
let (up, down, left, right) = match resize_edge {
|
let (up, down, left, right) = match resize_edge {
|
||||||
ResizeEdge::Top => (true, false, false, false),
|
ResizeEdge::Top => (true, false, false, false),
|
||||||
@@ -239,12 +253,16 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
|
|
||||||
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
let _ = window.set_fullscreen(true);
|
let _ = window.set_fullscreen(true);
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
let Some(panel_item) = self.panel_item(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
panel_item.toplevel_fullscreen_active(true);
|
panel_item.toplevel_fullscreen_active(true);
|
||||||
}
|
}
|
||||||
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
let _ = window.set_fullscreen(false);
|
let _ = window.set_fullscreen(false);
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
let Some(panel_item) = self.panel_item(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
panel_item.toplevel_fullscreen_active(true);
|
panel_item.toplevel_fullscreen_active(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,63 +291,6 @@ impl X11Backend {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
impl Backend for X11Backend {
|
impl Backend for X11Backend {
|
||||||
// fn start_data(&self, id: &str) -> Result<Message> {
|
|
||||||
// let size = (
|
|
||||||
// self.toplevel.geometry().size.w as u32,
|
|
||||||
// self.toplevel.geometry().size.h as u32,
|
|
||||||
// );
|
|
||||||
// let toplevel_state = (
|
|
||||||
// None::<String>,
|
|
||||||
// self.toplevel.title(),
|
|
||||||
// None::<String>,
|
|
||||||
// (
|
|
||||||
// self.toplevel.geometry().size.w as u32,
|
|
||||||
// self.toplevel.geometry().size.h as u32,
|
|
||||||
// ),
|
|
||||||
// self.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)),
|
|
||||||
// self.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)),
|
|
||||||
// ((0_i32, 0_i32), size),
|
|
||||||
// vec![0_u32; 0],
|
|
||||||
// );
|
|
||||||
// let info = (
|
|
||||||
// None::<(Vector2<u32>, Vector2<i32>)>,
|
|
||||||
// toplevel_state,
|
|
||||||
// Vec::<PopupData>::new(),
|
|
||||||
// None::<SurfaceID>,
|
|
||||||
// None::<SurfaceID>,
|
|
||||||
// );
|
|
||||||
// Ok(serialize((id, info))?.into())
|
|
||||||
// }
|
|
||||||
// fn serialize_toplevel(&self) -> Result<Message> {
|
|
||||||
// let toplevel_state = (
|
|
||||||
// None::<String>,
|
|
||||||
// self.toplevel.title(),
|
|
||||||
// None::<String>,
|
|
||||||
// (
|
|
||||||
// self.toplevel.geometry().size.w,
|
|
||||||
// self.toplevel.geometry().size.h,
|
|
||||||
// ),
|
|
||||||
// self.toplevel.min_size().map(|s| (s.w, s.h)),
|
|
||||||
// self.toplevel.max_size().map(|s| (s.w, s.w)),
|
|
||||||
// );
|
|
||||||
// let data = serialize(&toplevel_state)?;
|
|
||||||
// Ok(data.into())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn set_toplevel_capabilities(&self, _capabilities: Vec<u8>) {}
|
|
||||||
|
|
||||||
// fn set_toplevel_size(
|
|
||||||
// &self,
|
|
||||||
// size: Option<Vector2<u32>>,
|
|
||||||
// states: Vec<u32>,
|
|
||||||
// _bounds: Option<Vector2<u32>>,
|
|
||||||
// ) {
|
|
||||||
// let _ = self.toplevel.configure(
|
|
||||||
// size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))),
|
|
||||||
// );
|
|
||||||
// let _ = self.toplevel.set_maximized(states.contains(&1));
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn start_data(&self) -> Result<PanelItemInitData> {
|
fn start_data(&self) -> Result<PanelItemInitData> {
|
||||||
Ok(PanelItemInitData {
|
Ok(PanelItemInitData {
|
||||||
cursor: None,
|
cursor: None,
|
||||||
@@ -364,7 +325,9 @@ impl Backend for X11Backend {
|
|||||||
keyboard_grab: self._keyboard_grab.lock().clone(),
|
keyboard_grab: self._keyboard_grab.lock().clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn close_toplevel(&self) {}
|
fn close_toplevel(&self) {
|
||||||
|
let _ = self.toplevel.close();
|
||||||
|
}
|
||||||
|
|
||||||
fn auto_size_toplevel(&self) {
|
fn auto_size_toplevel(&self) {
|
||||||
let _ = self.toplevel.configure(None);
|
let _ = self.toplevel.configure(None);
|
||||||
@@ -380,19 +343,27 @@ impl Backend for X11Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
||||||
let Some(wl_surface) = self.wl_surface_from_id(&surface) else {return};
|
let Some(wl_surface) = self.wl_surface_from_id(&surface) else {
|
||||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return};
|
return;
|
||||||
|
};
|
||||||
|
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
core_surface.apply_material(model_part);
|
core_surface.apply_material(model_part);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.seat
|
self.seat
|
||||||
.pointer_event(&surface, PointerEvent::Motion(position));
|
.pointer_event(&surface, PointerEvent::Motion(position));
|
||||||
}
|
}
|
||||||
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.seat.pointer_event(
|
self.seat.pointer_event(
|
||||||
&surface,
|
&surface,
|
||||||
PointerEvent::Button {
|
PointerEvent::Button {
|
||||||
@@ -407,7 +378,9 @@ impl Backend for X11Backend {
|
|||||||
scroll_distance: Option<Vector2<f32>>,
|
scroll_distance: Option<Vector2<f32>>,
|
||||||
scroll_steps: Option<Vector2<f32>>,
|
scroll_steps: Option<Vector2<f32>>,
|
||||||
) {
|
) {
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.seat.pointer_event(
|
self.seat.pointer_event(
|
||||||
&surface,
|
&surface,
|
||||||
PointerEvent::Scroll {
|
PointerEvent::Scroll {
|
||||||
@@ -418,9 +391,13 @@ impl Backend for X11Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) {
|
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) {
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let keymaps = KEYMAPS.lock();
|
let keymaps = KEYMAPS.lock();
|
||||||
let Some(keymap) = keymaps.get(keymap_id).cloned() else {return};
|
let Some(keymap) = keymaps.get(keymap_id).cloned() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
if self.seat.set_keymap(keymap, vec![surface.clone()]).is_err() {
|
if self.seat.set_keymap(keymap, vec![surface.clone()]).is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -429,9 +406,25 @@ impl Backend for X11Backend {
|
|||||||
&surface,
|
&surface,
|
||||||
KeyboardEvent::Key {
|
KeyboardEvent::Key {
|
||||||
key: key.abs() as u32,
|
key: key.abs() as u32,
|
||||||
state: if key < 0 { 1 } else { 0 },
|
state: key < 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.seat.touch_down(&surface, id, position)
|
||||||
|
}
|
||||||
|
fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
||||||
|
self.seat.touch_move(id, position)
|
||||||
|
}
|
||||||
|
fn touch_up(&self, id: u32) {
|
||||||
|
self.seat.touch_up(id)
|
||||||
|
}
|
||||||
|
fn reset_touches(&self) {
|
||||||
|
self.seat.reset_touches()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user