Compare commits
188 Commits
camera-ite
...
0.42.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
46
.github/workflows/build.yml
vendored
46
.github/workflows/build.yml
vendored
@@ -1,46 +0,0 @@
|
|||||||
name: Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_and_package:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install runtime dependencies
|
|
||||||
run: sudo apt install -y --no-install-recommends libxkbcommon-dev libstdc++6 libopenxr-dev libx11-dev libxfixes-dev libgl1-mesa-dev libegl1-mesa-dev libgbm-dev libfontconfig-dev libjsoncpp-dev libxcb1-dev libglx-dev libxcb-glx0-dev libdrm-dev libwayland-dev libfreetype-dev libpng-dev
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
|
||||||
run: sudo apt install -y --no-install-recommends cmake ninja-build
|
|
||||||
|
|
||||||
- name: Set up Rust
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
|
|
||||||
- name: Build server
|
|
||||||
run: cargo build --release
|
|
||||||
|
|
||||||
|
|
||||||
- name: Install appimagetool
|
|
||||||
run: |
|
|
||||||
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$(uname -m).AppImage -O /usr/local/bin/appimagetool; \
|
|
||||||
chmod +x /usr/local/bin/appimagetool; \
|
|
||||||
sed -i 's|AI\x02|\x00\x00\x00|' /usr/local/bin/appimagetool
|
|
||||||
- name: Install cargo-appimage
|
|
||||||
run: cargo install cargo-appimage
|
|
||||||
|
|
||||||
- name: Generate AppImage
|
|
||||||
run: cargo appimage
|
|
||||||
|
|
||||||
- name: Upload AppImage
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: appimage
|
|
||||||
path: '*.AppImage'
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,4 +12,3 @@
|
|||||||
|
|
||||||
/libs/
|
/libs/
|
||||||
*.AppImage
|
*.AppImage
|
||||||
*.blend1
|
|
||||||
1369
Cargo.lock
generated
1369
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
55
Cargo.toml
55
Cargo.toml
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "stardust-xr-server"
|
name = "stardust-xr-server"
|
||||||
version = "0.43.0"
|
version = "0.42.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"
|
||||||
@@ -15,9 +15,8 @@ path = "src/main.rs"
|
|||||||
[features]
|
[features]
|
||||||
default = ["wayland"]
|
default = ["wayland"]
|
||||||
wayland = ["dep:smithay", "dep:xkbcommon"]
|
wayland = ["dep:smithay", "dep:xkbcommon"]
|
||||||
xwayland = ["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-chrome"]
|
||||||
|
|
||||||
[package.metadata.appimage]
|
[package.metadata.appimage]
|
||||||
auto_link = true
|
auto_link = true
|
||||||
@@ -39,6 +38,7 @@ lto = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
color-eyre = { version = "0.6.2", default-features = false }
|
color-eyre = { version = "0.6.2", default-features = false }
|
||||||
clap = { version = "4.2.4", features = ["derive"] }
|
clap = { version = "4.2.4", features = ["derive"] }
|
||||||
|
dashmap = "5.4.0"
|
||||||
glam = { version = "0.23.0", features = ["mint"] }
|
glam = { version = "0.23.0", features = ["mint"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
mint = "0.5.9"
|
mint = "0.5.9"
|
||||||
@@ -47,56 +47,45 @@ once_cell = "1.17.1"
|
|||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
portable-atomic = { version = "1.2.0", features = ["float", "std"] }
|
portable-atomic = { version = "1.2.0", features = ["float", "std"] }
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal", "time"] }
|
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal"] }
|
||||||
send_wrapper = "0.6.0"
|
send_wrapper = "0.6.0"
|
||||||
prisma = "0.1.1"
|
prisma = "0.1.1"
|
||||||
|
xkbcommon = { version = "0.5.0", default-features = false, optional = true }
|
||||||
|
stardust-xr = "0.11.4"
|
||||||
directories = "5.0.0"
|
directories = "5.0.0"
|
||||||
serde = { version = "1.0.160", features = ["derive"] }
|
serde = { version = "1.0.160", features = ["derive"] }
|
||||||
serde_repr = "0.1.16"
|
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
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"
|
|
||||||
xkbcommon = { version = "0.6.0", default-features = false, optional = true }
|
|
||||||
ctrlc = "3.4.1"
|
|
||||||
libc = "0.2.148"
|
|
||||||
input-event-codes = "5.16.8"
|
|
||||||
drm-fourcc = { version = "2.2.0", features = ["serde"] }
|
|
||||||
|
|
||||||
[dependencies.smithay]
|
|
||||||
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
|
||||||
git = "https://github.com/smithay/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
|
||||||
# path = "../smithay"
|
|
||||||
default-features = false
|
|
||||||
features = [
|
|
||||||
"desktop",
|
|
||||||
"backend_drm",
|
|
||||||
"backend_egl",
|
|
||||||
"renderer_gl",
|
|
||||||
"wayland_frontend",
|
|
||||||
]
|
|
||||||
version = "*"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.stereokit]
|
[dependencies.stereokit]
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["linux-egl"]
|
features = ["linux-egl"]
|
||||||
version = "0.16.9"
|
version = "0.16.7"
|
||||||
|
|
||||||
|
[dependencies.smithay]
|
||||||
|
git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||||
|
# git = "https://github.com/smithay/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||||
|
# path = "../smithay"
|
||||||
|
default-features = false
|
||||||
|
features = ["desktop", "renderer_gl", "wayland_frontend"]
|
||||||
|
version = "*"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.console-subscriber]
|
[dependencies.console-subscriber]
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.tracing-tracy]
|
[dependencies.tracing-chrome]
|
||||||
version = "0.10.4"
|
version = "0.7.1"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.stardust-xr]
|
|
||||||
git = "https://github.com/StardustXR/core.git"
|
|
||||||
branch = "camera-item"
|
|
||||||
|
|
||||||
# [patch.crates-io.stereokit]
|
# [patch.crates-io.stereokit]
|
||||||
# path = "../stereokit-rs"
|
# path = "../stereokit-rs"
|
||||||
# [patch.crates-io.stereokit-sys]
|
# [patch.crates-io.stereokit-sys]
|
||||||
# path = "../stereokit-sys"
|
# path = "../stereokit-sys"
|
||||||
|
# [patch.crates-io.stardust-xr]
|
||||||
|
# path = "../core/core"
|
||||||
|
# [patch.crates-io.stardust-xr-schemas]
|
||||||
|
# path = "../core/schemas"
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -15,52 +15,11 @@ This project is a usable Linux display server that reinvents human-computer inte
|
|||||||
```bash
|
```bash
|
||||||
cargo build
|
cargo build
|
||||||
```
|
```
|
||||||
The latest stable server is automatically built to an appimage at https://github.com/StardustXR/server/releases for easy testing.
|
|
||||||
|
|
||||||
## Usage
|
## Install
|
||||||
|
```bash
|
||||||
First, try running `cargo run` in a terminal window. If a headset is plugged in and OpenXR is working no window will show up. However, the headset should show the same things as the window that opens:
|
cargo install
|
||||||
|
```
|
||||||

|
|
||||||
|
|
||||||
Stardust won't do anything interesting without clients! Try some from https://github.com/StardustXR.
|
|
||||||
|
|
||||||
### Default Sky
|
|
||||||
|
|
||||||
You can set a default skytex/skylight by putting your favorite HDRI equirectangular sky in `~/.config/stardust/skytex.hdr`. Certain clients can override this.
|
|
||||||
|
|
||||||
Flatscreen mode when the default skybox is [Zhengyang Gate](https://polyhaven.com/a/zhengyang_gate):
|
|
||||||

|
|
||||||
|
|
||||||
### Windowed Mode
|
|
||||||
|
|
||||||
If the stardust server can't connect to an OpenXR runtime or you force it into flatscreen mode with `-f`, the server will show in a window.
|
|
||||||

|
|
||||||
|
|
||||||
You can navigate around by right click + dragging to look around, Shift+W/A/S/D/Q/E to move. If you have a virtual hand, left click pinches, right click points, both make a fist.
|
|
||||||
|
|
||||||
### Flags
|
|
||||||
#### Flatscreen (-f)
|
|
||||||
|
|
||||||
The server will show up in windowed mode no matter what with your mouse pointer being turned into a 3D pointer. Keyboard input will be sent to whatever your mouse is hovering over like visionOS simulator.
|
|
||||||
Flatscreen mode upon initial startup:
|
|
||||||

|
|
||||||
|
|
||||||
#### Overlay (-o \<PRIORITY>)
|
|
||||||
|
|
||||||
The server will, if in XR mode, be overlaid using the OpenXR overlay extension with the given priority.
|
|
||||||
|
|
||||||
#### Disable controller (--disable-controller)
|
|
||||||
|
|
||||||
Some runtimes such as Monado may emulate a controller using a hand, and this messes with Stardust's input system. Set this flag to ignore the controllers that the OpenXR runtime provides.
|
|
||||||
|
|
||||||
#### Execute (-e </path/to/executable>)
|
|
||||||
|
|
||||||
When wayland and OpenXR and such are initialized, run the given executable (such as a bash script) with all the environment variables needed to connect all clients of any type to the server. If not set, the server will run the executable at `~/.config/stardust/startup` if it exists. This is how stardust desktop environments can be made.
|
|
||||||
|
|
||||||
#### Help (-h, --help)
|
|
||||||
|
|
||||||
help
|
|
||||||
|
|
||||||
## Test
|
## Test
|
||||||
|
|
||||||
@@ -83,3 +42,4 @@ help
|
|||||||
##### Everything
|
##### Everything
|
||||||
|
|
||||||
`nix flake check` will build every test underneath of the `checks` attribute in the `flake.nix`
|
`nix flake check` will build every test underneath of the `checks` attribute in the `flake.nix`
|
||||||
|
|
||||||
|
|||||||
260
flake.lock
generated
260
flake.lock
generated
@@ -3,11 +3,32 @@
|
|||||||
"fenix": {
|
"fenix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"flatland",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1683786056,
|
||||||
|
"narHash": "sha256-Wrz/X9D0t8akhvEGj5a93xgpxI3vAcdPGcwn6tKHooc=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "5816c7bbcc385d2e65877631497df3f7d66b354a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fenix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"flatland",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src_2"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1678775037,
|
"lastModified": 1678775037,
|
||||||
"narHash": "sha256-chx0tWnXKpcayPkPY3Qh+2hNwptvX8XE3o+fYZ+GNzg=",
|
"narHash": "sha256-chx0tWnXKpcayPkPY3Qh+2hNwptvX8XE3o+fYZ+GNzg=",
|
||||||
@@ -22,34 +43,32 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673956053,
|
||||||
|
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-parts": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690933134,
|
"lastModified": 1678379998,
|
||||||
"narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=",
|
"narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb",
|
"rev": "c13d60b89adea3dc20704c045ec4d50dd964d447",
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-parts_2": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs-lib": "nixpkgs-lib_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1688466019,
|
|
||||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -57,7 +76,7 @@
|
|||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts_3": {
|
"flake-parts_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": [
|
||||||
"hercules-ci-effects",
|
"hercules-ci-effects",
|
||||||
@@ -66,11 +85,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688466019,
|
"lastModified": 1678379998,
|
||||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
"narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
"rev": "c13d60b89adea3dc20704c045ec4d50dd964d447",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -79,17 +98,32 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667395993,
|
||||||
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flatland": {
|
"flatland": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fenix": "fenix",
|
"fenix": "fenix_2",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1694190588,
|
"lastModified": 1683766358,
|
||||||
"narHash": "sha256-tcvp09A54AbXkkXS/P6Ed5TUWzMEuQ9h/fnfLz7gnJc=",
|
"narHash": "sha256-wX1Lpj95kkHUZAloB1fGs+ixaRycaOJq4F77+HvaJCQ=",
|
||||||
"owner": "StardustXR",
|
"owner": "StardustXR",
|
||||||
"repo": "flatland",
|
"repo": "flatland",
|
||||||
"rev": "3867067452761959a95497d6589f9336b347270c",
|
"rev": "24613a496841bdf38e5f136608d5295860a75fce",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -98,34 +132,59 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"haskell-flake": {
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hercules-ci-effects",
|
||||||
|
"hercules-ci-agent",
|
||||||
|
"pre-commit-hooks-nix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1684780604,
|
"lastModified": 1660459072,
|
||||||
"narHash": "sha256-2uMZsewmRn7rRtAnnQNw1lj0uZBMh4m6Cs/7dV5YF08=",
|
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||||
"owner": "srid",
|
"owner": "hercules-ci",
|
||||||
"repo": "haskell-flake",
|
"repo": "gitignore.nix",
|
||||||
"rev": "74210fa80a49f1b6f67223debdbf1494596ff9f2",
|
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "srid",
|
"owner": "hercules-ci",
|
||||||
"ref": "0.3.0",
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"haskell-flake": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1678138103,
|
||||||
|
"narHash": "sha256-D0lao82bV3t2gEFjHiU6RN233t+1MnkQV+bq8MEu2ic=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "haskell-flake",
|
||||||
|
"rev": "1e1660e6dd00838ba73bc7952e6e73be67da18d1",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"ref": "0.1-extraLibraries",
|
||||||
"repo": "haskell-flake",
|
"repo": "haskell-flake",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hercules-ci-agent": {
|
"hercules-ci-agent": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts_3",
|
"flake-parts": "flake-parts_2",
|
||||||
"haskell-flake": "haskell-flake",
|
"haskell-flake": "haskell-flake",
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nix-darwin": "nix-darwin",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"pre-commit-hooks-nix": "pre-commit-hooks-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688568579,
|
"lastModified": 1678446614,
|
||||||
"narHash": "sha256-ON0M56wtY/TIIGPkXDlJboAmuYwc73Hi8X9iJGtxOhM=",
|
"narHash": "sha256-Z6Gsba5ahn/N0QlF0vJfIEfnZgCs4qr1IZtXAqjbE7s=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "hercules-ci-agent",
|
"repo": "hercules-ci-agent",
|
||||||
"rev": "367dd8cd649b57009a6502e878005a1e54ad78c5",
|
"rev": "0b90d1a87c117a5861785cb85833dd1c9df0b6ef",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -135,16 +194,16 @@
|
|||||||
},
|
},
|
||||||
"hercules-ci-effects": {
|
"hercules-ci-effects": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts",
|
||||||
"hercules-ci-agent": "hercules-ci-agent",
|
"hercules-ci-agent": "hercules-ci-agent",
|
||||||
"nixpkgs": "nixpkgs_3"
|
"nixpkgs": "nixpkgs_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689397210,
|
"lastModified": 1681898675,
|
||||||
"narHash": "sha256-fVxZnqxMbsDkB4GzGAs/B41K0wt/e+B/fLxmTFF/S20=",
|
"narHash": "sha256-nIJ7CAdiHv4i1no/VgDoeTJLzbLYwu5+/Ycoyzn0S78=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "hercules-ci-effects",
|
"repo": "hercules-ci-effects",
|
||||||
"rev": "0a63bfa3f00a3775ea3a6722b247880f1ffe91ce",
|
"rev": "15ff4f63e5f28070391a5b09a82f6d5c6cc5c9d0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -153,6 +212,28 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nix-darwin": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hercules-ci-effects",
|
||||||
|
"hercules-ci-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673295039,
|
||||||
|
"narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=",
|
||||||
|
"owner": "LnL7",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"rev": "87b9d090ad39b25b2400029c64825fc2a8868943",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "LnL7",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1678703398,
|
"lastModified": 1678703398,
|
||||||
@@ -172,11 +253,11 @@
|
|||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "lib",
|
"dir": "lib",
|
||||||
"lastModified": 1690881714,
|
"lastModified": 1678375444,
|
||||||
"narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=",
|
"narHash": "sha256-XIgHfGvjFvZQ8hrkfocanCDxMefc/77rXeHvYdzBMc8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9e1960bc196baf6881340d53dccb203a951745a2",
|
"rev": "130fa0baaa2b93ec45523fdcde942f6844ee9f6e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -187,31 +268,29 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib_2": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "lib",
|
"lastModified": 1673800717,
|
||||||
"lastModified": 1688049487,
|
"narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
|
||||||
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
|
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
|
"rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"dir": "lib",
|
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-22.11",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688322751,
|
"lastModified": 1678293141,
|
||||||
"narHash": "sha256-eW62dC5f33oKZL7VWlomttbUnOTHrAbte9yNUNW8rbk=",
|
"narHash": "sha256-lLlQHaR0y+q6nd6kfpydPTGHhl1rS9nU9OQmztzKOYs=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0fbe93c5a7cac99f90b60bdf5f149383daaa615f",
|
"rev": "c90c4025bb6e0c4eaf438128a3b2640314b1c58d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -223,11 +302,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689393711,
|
"lastModified": 1678891326,
|
||||||
"narHash": "sha256-l3gyTPy/qWLDk1CY1EgYwlsxcGxN2aVd7MlCzQa69rk=",
|
"narHash": "sha256-cjgrjKx7y+hO9I8O2b6QvBaTt9w7Xhk/5hsnJYTUb2I=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "27fcd46fa18df36d270174246e7bd8f1787129ff",
|
"rev": "1544ef240132d4357d9a39a40c8e6afd1678b052",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -238,29 +317,72 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1692734709,
|
"lastModified": 1683408522,
|
||||||
"narHash": "sha256-SCFnyHCyYjwEmgUsHDDuU0TsbVMKeU1vwkR+r7uS2Rg=",
|
"narHash": "sha256-9kcPh6Uxo17a3kK3XCHhcWiV1Yu1kYj22RHiymUhMkU=",
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b85ed9dcbf187b909ef7964774f8847d554fab3b",
|
"rev": "897876e4c484f1e8f92009fd11b7d988a121a4e7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pre-commit-hooks-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"hercules-ci-effects",
|
||||||
|
"hercules-ci-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1678376203,
|
||||||
|
"narHash": "sha256-3tyYGyC8h7fBwncLZy5nCUjTJPrHbmNwp47LlNLOHSM=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"rev": "1a20b9708962096ec2481eeb2ddca29ed747770a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts",
|
"fenix": "fenix",
|
||||||
"flatland": "flatland",
|
"flatland": "flatland",
|
||||||
"hercules-ci-effects": "hercules-ci-effects",
|
"hercules-ci-effects": "hercules-ci-effects",
|
||||||
"nixpkgs": "nixpkgs_4"
|
"nixpkgs": "nixpkgs_4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1683653808,
|
||||||
|
"narHash": "sha256-GiKwJySG/YCPIKwz9wSm9fJa5e4CU3GvTYAKZzjBhFo=",
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "b7cdd93f3e1533e96d4cfa1ac8573e6210a2bedf",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src_2": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1678695923,
|
"lastModified": 1678695923,
|
||||||
|
|||||||
183
flake.nix
183
flake.nix
@@ -3,77 +3,130 @@
|
|||||||
extra-substituters = [ "https://stardustxr.cachix.org" ];
|
extra-substituters = [ "https://stardustxr.cachix.org" ];
|
||||||
extra-trusted-public-keys = [ "stardustxr.cachix.org-1:mWSn8Ap2RLsIWT/8gsj+VfbJB6xoOkPaZpbjO+r9HBo=" ];
|
extra-trusted-public-keys = [ "stardustxr.cachix.org-1:mWSn8Ap2RLsIWT/8gsj+VfbJB6xoOkPaZpbjO+r9HBo=" ];
|
||||||
};
|
};
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
|
||||||
hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects";
|
|
||||||
|
|
||||||
# Since we do not have a monorepo, we have to fetch Flatland in order to use
|
# 22.11 does not include PR #218472, hence we use the unstable version
|
||||||
# it to create VM Tests
|
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
|
||||||
flatland.url = "github:StardustXR/flatland";
|
|
||||||
};
|
# Since we do not have a monorepo, we have to fetch Flatland in order to use
|
||||||
outputs = inputs@{ self, flake-parts, nixpkgs, hercules-ci-effects, flatland, ... }:
|
# it to create VM Tests
|
||||||
|
inputs.flatland.url = "github:StardustXR/flatland";
|
||||||
|
|
||||||
|
inputs.fenix.url = github:nix-community/fenix;
|
||||||
|
inputs.fenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
|
inputs.hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, fenix, hercules-ci-effects, flatland, ... }:
|
||||||
let
|
let
|
||||||
name = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
|
name = "server";
|
||||||
src = builtins.path {
|
pkgs = system: import nixpkgs {
|
||||||
name = "${name}-source";
|
inherit system;
|
||||||
path = toString ./.;
|
|
||||||
filter = path: type:
|
|
||||||
nixpkgs.lib.all
|
|
||||||
(n: builtins.baseNameOf path != n)
|
|
||||||
[
|
|
||||||
"flake.nix"
|
|
||||||
"flake.lock"
|
|
||||||
"nix"
|
|
||||||
"README.md"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
in
|
shell = pkgs: pkgs.mkShell {
|
||||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
inputsFrom = [ self.packages.${pkgs.system}.default ];
|
||||||
imports = [
|
|
||||||
flake-parts.flakeModules.easyOverlay
|
# ---- START package specific dev settings ----
|
||||||
];
|
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
||||||
systems = [ "aarch64-linux" "x86_64-linux" "riscv64-linux" ];
|
# ---- END package specific dev settings ----
|
||||||
perSystem = { config, self', inputs', pkgs, system, ... }: {
|
|
||||||
_module.args.pkgs = import inputs.nixpkgs { inherit system; overlays = [ inputs.self.overlays.default ]; };
|
|
||||||
overlayAttrs = config.packages;
|
|
||||||
packages = {
|
|
||||||
default = self'.packages.${name};
|
|
||||||
gnome-graphical-test = self'.checks.gnome-graphical-test;
|
|
||||||
"${name}" = pkgs.callPackage ./nix/stardust-xr-server.nix { inherit name src; };
|
|
||||||
};
|
|
||||||
checks.gnome-graphical-test = pkgs.nixosTest (import ./nix/gnome-graphical-test.nix { inherit pkgs self; });
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
inputsFrom = [ self'.packages.default ];
|
|
||||||
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
flake = {
|
package = pkgs:
|
||||||
herculesCI.ciSystems = [ "x86_64-linux" ];
|
let
|
||||||
effects = let
|
toolchain = fenix.packages.${pkgs.system}.minimal.toolchain;
|
||||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
in
|
||||||
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
(pkgs.makeRustPlatform {
|
||||||
in { branch, rev, ... }: {
|
cargo = toolchain;
|
||||||
gnome-graphical-test = hci-effects.mkEffect {
|
rustc = toolchain;
|
||||||
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
}).buildRustPackage rec {
|
||||||
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
pname = "stardust-xr-${name}";
|
||||||
effectScript = ''
|
src = builtins.path {
|
||||||
readSecretString stardustxrDiscord .webhook > .webhook
|
name = "stardust-xr-source";
|
||||||
readSecretString stardustxrIpfs .basicauth > .basicauth
|
path = toString ./.;
|
||||||
set -x
|
filter = path: type:
|
||||||
export RESPONSE=$(curl -H @.basicauth -F file=@${self.packages."x86_64-linux".gnome-graphical-test}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
|
nixpkgs.lib.all
|
||||||
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
|
(n: builtins.baseNameOf path != n)
|
||||||
set +x
|
[
|
||||||
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
"flake.nix"
|
||||||
${pkgs.discord-sh}/bin/discord.sh \
|
"flake.lock"
|
||||||
--description "\`stardustxr/server\` has been modified, here's how it renders \`weston-cliptest\` on \`flatland\` via \`monado-service\` inside of the \`gnome-graphical-test\`" \
|
"nix"
|
||||||
--field "Branch;${branch}" \
|
"README.md"
|
||||||
--field "Commit ID;${rev}" \
|
];
|
||||||
--field "Flatland Revision;${flatland.rev}" \
|
};
|
||||||
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
|
||||||
--image "$ADDRESS"
|
# ---- START package specific settings ----
|
||||||
|
version = "0.10.2";
|
||||||
|
|
||||||
|
cargoLock = {
|
||||||
|
lockFile = ./Cargo.lock;
|
||||||
|
allowBuiltinFetchGit = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
sk=$(echo $cargoDepsCopy/stereokit-sys-*/StereoKit)
|
||||||
|
mkdir -p $sk/build/cpm
|
||||||
|
cp ${pkgs.fetchurl {
|
||||||
|
url = "https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.32.2/CPM.cmake";
|
||||||
|
hash = "sha256-yDHlpqmpAE8CWiwJRoWyaqbuBAg0090G8WJIC2KLHp8=";
|
||||||
|
}} $sk/build/cpm/CPM_0.32.2.cmake
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
CPM_SOURCE_CACHE = "./build";
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
cmake pkg-config llvmPackages.libcxxClang
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
openxr-loader libGL mesa xorg.libX11 fontconfig libxkbcommon
|
||||||
|
];
|
||||||
|
|
||||||
|
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
||||||
|
# ---- END package specific settings ----
|
||||||
};
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
overlays.default = final: prev: {
|
||||||
|
stardust-xr = (prev.stardust-xr or {}) // {
|
||||||
|
${name} = package final;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
packages."x86_64-linux".default = package (pkgs "x86_64-linux");
|
||||||
|
packages."aarch64-linux".default = package (pkgs "aarch64-linux");
|
||||||
|
|
||||||
|
packages."x86_64-linux".gnome-graphical-test = self.checks.x86_64-linux.gnome-graphical-test;
|
||||||
|
packages."aarch64-linux".gnome-graphical-test = self.checks.aarch64-linux.gnome-graphical-test;
|
||||||
|
|
||||||
|
checks."x86_64-linux".gnome-graphical-test = (pkgs "x86_64-linux").nixosTest (import ./nix/gnome-graphical-test.nix { pkgs = (pkgs "x86_64-linux"); inherit self; });
|
||||||
|
checks."aarch64-linux".gnome-graphical-test = (pkgs "aarch64-linux").nixosTest (import ./nix/gnome-graphical-test.nix { pkgs = (pkgs "aarch64-linux"); inherit self; });
|
||||||
|
|
||||||
|
devShells."x86_64-linux".default = shell (pkgs "x86_64-linux");
|
||||||
|
devShells."aarch64-linux".default = shell (pkgs "aarch64-linux");
|
||||||
|
|
||||||
|
herculesCI.ciSystems = [ "x86_64-linux" ];
|
||||||
|
|
||||||
|
effects = let
|
||||||
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||||
|
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
|
||||||
|
in { branch, rev, ... }: {
|
||||||
|
gnome-graphical-test = hci-effects.mkEffect {
|
||||||
|
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
|
||||||
|
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
|
||||||
|
effectScript = ''
|
||||||
|
readSecretString stardustxrDiscord .webhook > .webhook
|
||||||
|
readSecretString stardustxrIpfs .basicauth > .basicauth
|
||||||
|
set -x
|
||||||
|
export RESPONSE=$(curl -H @.basicauth -F file=@${self.packages."x86_64-linux".gnome-graphical-test}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
|
||||||
|
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
|
||||||
|
set +x
|
||||||
|
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
|
||||||
|
${pkgs.discord-sh}/bin/discord.sh \
|
||||||
|
--description "\`stardustxr/server\` has been modified, here's how it renders \`weston-cliptest\` on \`flatland\` via \`monado-service\` inside of the \`gnome-graphical-test\`" \
|
||||||
|
--field "Branch;${branch}" \
|
||||||
|
--field "Commit ID;${rev}" \
|
||||||
|
--field "Flatland Revision;${flatland.rev}" \
|
||||||
|
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
|
||||||
|
--image "$ADDRESS"
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 789 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,40 +0,0 @@
|
|||||||
{ rustPlatform
|
|
||||||
, src
|
|
||||||
, name
|
|
||||||
, openxr-loader
|
|
||||||
, libGL
|
|
||||||
, mesa
|
|
||||||
, xorg
|
|
||||||
, fontconfig
|
|
||||||
, libxkbcommon
|
|
||||||
, libclang
|
|
||||||
, cmake
|
|
||||||
, cpm-cmake
|
|
||||||
, pkg-config
|
|
||||||
, llvmPackages
|
|
||||||
}:
|
|
||||||
|
|
||||||
rustPlatform.buildRustPackage rec {
|
|
||||||
inherit src name;
|
|
||||||
cargoLock = {
|
|
||||||
lockFile = (src + "/Cargo.lock");
|
|
||||||
allowBuiltinFetchGit = true;
|
|
||||||
};
|
|
||||||
CPM_SOURCE_CACHE = "./build";
|
|
||||||
postPatch = ''
|
|
||||||
sk=$(echo $cargoDepsCopy/stereokit-sys-*/StereoKit)
|
|
||||||
mkdir -p $sk/build/cpm
|
|
||||||
|
|
||||||
# This is not ideal, the original approach was to fetch the exact cmake
|
|
||||||
# file version that was wanted from GitHub directly, but at least this way it comes from Nixpkgs.. so meh
|
|
||||||
cp ${cpm-cmake}/share/cpm/CPM.cmake $sk/build/cpm/CPM_0.32.2.cmake
|
|
||||||
'';
|
|
||||||
nativeBuildInputs = [
|
|
||||||
cmake pkg-config llvmPackages.libcxxClang
|
|
||||||
];
|
|
||||||
buildInputs = [
|
|
||||||
openxr-loader libGL mesa xorg.libX11 fontconfig libxkbcommon
|
|
||||||
];
|
|
||||||
LIBCLANG_PATH = "${libclang.lib}/lib";
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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,20 +1,20 @@
|
|||||||
use super::{
|
use super::scenegraph::Scenegraph;
|
||||||
client_state::{ClientState, CLIENT_STATES},
|
|
||||||
scenegraph::Scenegraph,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{registry::OwnedRegistry, task},
|
core::{registry::OwnedRegistry, task},
|
||||||
nodes::{audio, data, drawable, fields, hmd, input, items, root::Root, spatial, Node},
|
nodes::{
|
||||||
|
audio, data, drawable, fields, hmd, input, items,
|
||||||
|
root::Root,
|
||||||
|
spatial,
|
||||||
|
startup::{self, StartupSettings, STARTUP_SETTINGS},
|
||||||
|
Node,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stardust_xr::{
|
use stardust_xr::messenger::{self, MessageSenderHandle};
|
||||||
messenger::{self, MessageSenderHandle},
|
|
||||||
schemas::flex::serialize,
|
|
||||||
};
|
|
||||||
use std::{fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
use std::{fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
||||||
use tokio::{net::UnixStream, task::JoinHandle};
|
use tokio::{net::UnixStream, task::JoinHandle};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@@ -34,7 +34,7 @@ lazy_static! {
|
|||||||
scenegraph: Default::default(),
|
scenegraph: Default::default(),
|
||||||
root: OnceCell::new(),
|
root: OnceCell::new(),
|
||||||
base_resource_prefixes: Default::default(),
|
base_resource_prefixes: Default::default(),
|
||||||
state: Arc::new(ClientState::default()),
|
startup_settings: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,13 +46,13 @@ pub fn get_env(pid: i32) -> Result<FxHashMap<String, String>, std::io::Error> {
|
|||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
.map(|(k, v)| (k.to_string(), v.to_string())),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
pub fn state(env: &FxHashMap<String, String>) -> Option<Arc<ClientState>> {
|
pub fn startup_settings(env: &FxHashMap<String, String>) -> Option<StartupSettings> {
|
||||||
let token = env.get("STARDUST_STARTUP_TOKEN")?;
|
let token = env.get("STARDUST_STARTUP_TOKEN")?;
|
||||||
CLIENT_STATES.lock().get(token).cloned()
|
STARTUP_SETTINGS.lock().get(token).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub pid: Option<i32>,
|
pid: Option<i32>,
|
||||||
// env: Option<FxHashMap<String, String>>,
|
// env: Option<FxHashMap<String, String>>,
|
||||||
exe: Option<PathBuf>,
|
exe: Option<PathBuf>,
|
||||||
dispatch_join_handle: OnceCell<JoinHandle<Result<()>>>,
|
dispatch_join_handle: OnceCell<JoinHandle<Result<()>>>,
|
||||||
@@ -63,7 +63,7 @@ pub struct Client {
|
|||||||
pub scenegraph: Arc<Scenegraph>,
|
pub scenegraph: Arc<Scenegraph>,
|
||||||
pub root: OnceCell<Arc<Root>>,
|
pub root: OnceCell<Arc<Root>>,
|
||||||
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
|
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
|
||||||
pub state: Arc<ClientState>,
|
pub startup_settings: Option<StartupSettings>,
|
||||||
}
|
}
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
|
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
|
||||||
@@ -80,10 +80,7 @@ impl Client {
|
|||||||
|
|
||||||
let (mut messenger_tx, mut messenger_rx) = messenger::create(connection);
|
let (mut messenger_tx, mut messenger_rx) = messenger::create(connection);
|
||||||
let scenegraph = Arc::new(Scenegraph::default());
|
let scenegraph = Arc::new(Scenegraph::default());
|
||||||
let state = env
|
let startup_settings = env.as_ref().and_then(startup_settings);
|
||||||
.as_ref()
|
|
||||||
.and_then(state)
|
|
||||||
.unwrap_or_else(|| Arc::new(ClientState::default()));
|
|
||||||
|
|
||||||
let client = CLIENTS.add(Client {
|
let client = CLIENTS.add(Client {
|
||||||
pid,
|
pid,
|
||||||
@@ -98,7 +95,7 @@ impl Client {
|
|||||||
scenegraph: scenegraph.clone(),
|
scenegraph: scenegraph.clone(),
|
||||||
root: OnceCell::new(),
|
root: OnceCell::new(),
|
||||||
base_resource_prefixes: Default::default(),
|
base_resource_prefixes: Default::default(),
|
||||||
state,
|
startup_settings,
|
||||||
});
|
});
|
||||||
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
|
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
|
||||||
let _ = client.root.set(Root::create(&client)?);
|
let _ = client.root.set(Root::create(&client)?);
|
||||||
@@ -110,13 +107,7 @@ impl Client {
|
|||||||
data::create_interface(&client)?;
|
data::create_interface(&client)?;
|
||||||
items::create_interface(&client)?;
|
items::create_interface(&client)?;
|
||||||
input::create_interface(&client)?;
|
input::create_interface(&client)?;
|
||||||
|
startup::create_interface(&client)?;
|
||||||
client
|
|
||||||
.root
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.node
|
|
||||||
.send_remote_signal("restore_state", serialize(client.state.apply_to(&client))?)?;
|
|
||||||
|
|
||||||
let pid_printable = pid
|
let pid_printable = pid
|
||||||
.map(|pid| pid.to_string())
|
.map(|pid| pid.to_string())
|
||||||
@@ -173,27 +164,6 @@ impl Client {
|
|||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cmdline(&self) -> Option<Vec<String>> {
|
|
||||||
let pid = self.pid?;
|
|
||||||
let exe_proc_path = format!("/proc/{pid}/exe");
|
|
||||||
let cmdline_proc_path = format!("/proc/{pid}/cmdline");
|
|
||||||
let exe = std::fs::read_link(exe_proc_path).ok()?;
|
|
||||||
let cmdline = std::fs::read_to_string(cmdline_proc_path).ok()?;
|
|
||||||
let mut cmdline_split: Vec<_> = cmdline.split('\0').map(ToString::to_string).collect();
|
|
||||||
cmdline_split.pop();
|
|
||||||
*cmdline_split.get_mut(0).unwrap() = exe.to_str()?.to_string();
|
|
||||||
Some(cmdline_split)
|
|
||||||
}
|
|
||||||
pub fn get_cwd(&self) -> Option<PathBuf> {
|
|
||||||
let pid = self.pid?;
|
|
||||||
let cwd_proc_path = format!("/proc/{pid}/cwd");
|
|
||||||
std::fs::read_link(cwd_proc_path).ok()
|
|
||||||
}
|
|
||||||
pub async fn save_state(&self) -> Option<ClientState> {
|
|
||||||
let internal = self.root.get()?.save_state().await.ok()?;
|
|
||||||
Some(ClientState::from_deserialized(self, internal))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_node(&self, name: &'static str, path: &str) -> Result<Arc<Node>> {
|
pub fn get_node(&self, name: &'static str, path: &str) -> Result<Arc<Node>> {
|
||||||
self.scenegraph
|
self.scenegraph
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
use super::client::{get_env, Client};
|
|
||||||
use crate::nodes::{spatial::Spatial, Node};
|
|
||||||
use glam::Mat4;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::{io::Write, path::PathBuf, sync::Arc};
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientState>>> = Default::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct LaunchInfo {
|
|
||||||
pub cmdline: Vec<String>,
|
|
||||||
pub cwd: PathBuf,
|
|
||||||
pub env: FxHashMap<String, String>,
|
|
||||||
}
|
|
||||||
impl LaunchInfo {
|
|
||||||
fn from_client(client: &Client) -> Option<Self> {
|
|
||||||
Some(LaunchInfo {
|
|
||||||
cmdline: client.get_cmdline()?,
|
|
||||||
cwd: client.get_cwd()?,
|
|
||||||
env: get_env(client.pid?).ok()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ClientState {
|
|
||||||
pub launch_info: Option<LaunchInfo>,
|
|
||||||
pub data: Option<Vec<u8>>,
|
|
||||||
pub root: Mat4,
|
|
||||||
pub spatial_anchors: FxHashMap<String, Mat4>,
|
|
||||||
}
|
|
||||||
impl ClientState {
|
|
||||||
pub fn from_deserialized(client: &Client, state: ClientStateInternal) -> Self {
|
|
||||||
ClientState {
|
|
||||||
launch_info: LaunchInfo::from_client(client),
|
|
||||||
data: state.data,
|
|
||||||
root: Self::spatial_transform(client, &state.root.unwrap_or_default())
|
|
||||||
.unwrap_or_default(),
|
|
||||||
spatial_anchors: state
|
|
||||||
.spatial_anchors
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(k, v)| Some((k, Self::spatial_transform(client, &v)?)))
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn spatial_transform(client: &Client, path: &str) -> Option<Mat4> {
|
|
||||||
let node = client.scenegraph.get_node(path)?;
|
|
||||||
let spatial = node.spatial.get()?;
|
|
||||||
Some(spatial.global_transform())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn token(self) -> String {
|
|
||||||
let token = nanoid::nanoid!();
|
|
||||||
CLIENT_STATES.lock().insert(token.clone(), Arc::new(self));
|
|
||||||
token
|
|
||||||
}
|
|
||||||
pub fn to_file(self) {
|
|
||||||
let project_dirs = directories::ProjectDirs::from("", "", "stardust").unwrap();
|
|
||||||
let state_dir = project_dirs.state_dir().unwrap();
|
|
||||||
std::fs::create_dir_all(state_dir).unwrap();
|
|
||||||
let mut file = std::fs::File::create(state_dir.join(nanoid::nanoid!())).unwrap();
|
|
||||||
file.write_all(&stardust_xr::schemas::flex::flexbuffers::to_vec(self).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
pub fn apply_to(&self, client: &Arc<Client>) -> ClientStateInternal {
|
|
||||||
if let Some(root) = client.root.get() {
|
|
||||||
root.set_transform(self.root)
|
|
||||||
}
|
|
||||||
ClientStateInternal {
|
|
||||||
data: self.data.clone(),
|
|
||||||
root: Some("/".to_string()),
|
|
||||||
spatial_anchors: self
|
|
||||||
.spatial_anchors
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
(k.clone(), {
|
|
||||||
let node = Node::create(client, "/spatial/anchor", k, true)
|
|
||||||
.add_to_scenegraph()
|
|
||||||
.unwrap();
|
|
||||||
Spatial::add_to(&node, None, *v, false).unwrap();
|
|
||||||
k.clone()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for ClientState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
launch_info: None,
|
|
||||||
data: None,
|
|
||||||
root: Mat4::IDENTITY,
|
|
||||||
spatial_anchors: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
|
||||||
pub struct ClientStateInternal {
|
|
||||||
data: Option<Vec<u8>>,
|
|
||||||
root: Option<String>,
|
|
||||||
spatial_anchors: FxHashMap<String, String>,
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod client_state;
|
|
||||||
pub mod delta;
|
pub mod delta;
|
||||||
pub mod destroy_queue;
|
pub mod destroy_queue;
|
||||||
pub mod eventloop;
|
pub mod eventloop;
|
||||||
@@ -8,5 +7,3 @@ 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;
|
|
||||||
|
|||||||
@@ -7,32 +7,33 @@ use std::{
|
|||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
// #[derive(Default)]
|
#[derive(Default)]
|
||||||
// pub struct LifeLinkedNodeList {
|
pub struct LifeLinkedNodeList {
|
||||||
// nodes: Mutex<Vec<Weak<Node>>>,
|
nodes: Mutex<Vec<Weak<Node>>>,
|
||||||
// }
|
}
|
||||||
// impl LifeLinkedNodeList {
|
impl LifeLinkedNodeList {
|
||||||
// pub fn add(&self, node: Weak<Node>) {
|
pub fn add(&self, node: Weak<Node>) {
|
||||||
// self.nodes.lock().push(node);
|
self.nodes.lock().push(node);
|
||||||
// }
|
}
|
||||||
// pub fn clear(&self) {
|
|
||||||
// self.nodes
|
|
||||||
// .lock()
|
|
||||||
// .iter()
|
|
||||||
// .filter_map(|node| node.upgrade())
|
|
||||||
// .for_each(|node| {
|
|
||||||
// node.destroy();
|
|
||||||
// });
|
|
||||||
// self.nodes.lock().clear();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// impl Drop for LifeLinkedNodeList {
|
|
||||||
// fn drop(&mut self) {
|
|
||||||
// self.clear();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
pub fn clear(&self) {
|
||||||
|
self.nodes
|
||||||
|
.lock()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|node| node.upgrade())
|
||||||
|
.for_each(|node| {
|
||||||
|
node.destroy();
|
||||||
|
});
|
||||||
|
self.nodes.lock().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for LifeLinkedNodeList {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct LifeLinkedNodeMap<K: Hash + Eq> {
|
pub struct LifeLinkedNodeMap<K: Hash + Eq> {
|
||||||
nodes: Mutex<FxHashMap<K, Weak<Node>>>,
|
nodes: Mutex<FxHashMap<K, Weak<Node>>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,14 +54,6 @@ impl<T: Send + Sync + ?Sized> Registry<T> {
|
|||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.lock().clear();
|
self.lock().clear();
|
||||||
}
|
}
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
let registry = self.0.lock();
|
|
||||||
let Some(registry) = &*registry else {return true};
|
|
||||||
if registry.is_empty() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
registry.values().all(|v| v.strong_count() == 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl<T: Send + Sync + ?Sized> Clone for Registry<T> {
|
impl<T: Send + Sync + ?Sized> Clone for Registry<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
|
use crate::core::client::Client;
|
||||||
use crate::nodes::Node;
|
use crate::nodes::Node;
|
||||||
use crate::{core::client::Client, nodes::Message};
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use portable_atomic::Ordering;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use stardust_xr::scenegraph;
|
use stardust_xr::scenegraph;
|
||||||
use stardust_xr::scenegraph::ScenegraphError;
|
use stardust_xr::scenegraph::ScenegraphError;
|
||||||
use std::os::fd::OwnedFd;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use tokio::sync::oneshot;
|
use tracing::{debug, debug_span, instrument};
|
||||||
use tracing::{debug, debug_span};
|
|
||||||
|
use core::hash::BuildHasherDefault;
|
||||||
|
use dashmap::DashMap;
|
||||||
|
use rustc_hash::FxHasher;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Scenegraph {
|
pub struct Scenegraph {
|
||||||
pub(super) client: OnceCell<Weak<Client>>,
|
pub(super) client: OnceCell<Weak<Client>>,
|
||||||
nodes: Mutex<FxHashMap<String, Arc<Node>>>,
|
nodes: DashMap<String, Arc<Node>, BuildHasherDefault<FxHasher>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scenegraph {
|
impl Scenegraph {
|
||||||
@@ -31,60 +30,32 @@ impl Scenegraph {
|
|||||||
pub fn add_node_raw(&self, node: Arc<Node>) {
|
pub fn add_node_raw(&self, node: Arc<Node>) {
|
||||||
debug!(node = ?&*node, "Add node");
|
debug!(node = ?&*node, "Add node");
|
||||||
let path = node.get_path().to_string();
|
let path = node.get_path().to_string();
|
||||||
self.nodes.lock().insert(path, node);
|
self.nodes.insert(path, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
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.get(path)?.clone();
|
||||||
while let Some(alias) = node.alias.get() {
|
while let Some(alias) = node.alias.get() {
|
||||||
if alias.enabled.load(Ordering::Relaxed) {
|
node = alias.original.upgrade()?;
|
||||||
node = alias.original.upgrade()?;
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(node)
|
Some(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_node(&self, path: &str) -> Option<Arc<Node>> {
|
pub fn remove_node(&self, path: &str) -> Option<Arc<Node>> {
|
||||||
debug!(path, "Remove node");
|
debug!(path, "Remove node");
|
||||||
self.nodes.lock().remove(path)
|
let (_, node) = self.nodes.remove(path)?;
|
||||||
|
Some(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MethodResponseSender(oneshot::Sender<Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError>>);
|
|
||||||
impl MethodResponseSender {
|
|
||||||
pub fn send(self, t: Result<Message, ScenegraphError>) {
|
|
||||||
let _ = self.0.send(t.map(|m| (m.data, m.fds)));
|
|
||||||
}
|
|
||||||
pub fn wrap_sync<F: FnOnce() -> color_eyre::eyre::Result<Message>>(self, f: F) {
|
|
||||||
self.send(f().map_err(|e| ScenegraphError::MethodError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl scenegraph::Scenegraph for Scenegraph {
|
impl scenegraph::Scenegraph for Scenegraph {
|
||||||
fn send_signal(
|
fn send_signal(&self, path: &str, method: &str, data: &[u8]) -> Result<(), ScenegraphError> {
|
||||||
&self,
|
let Some(client) = self.get_client() else {return Err(ScenegraphError::SignalNotFound)};
|
||||||
path: &str,
|
|
||||||
method: &str,
|
|
||||||
data: &[u8],
|
|
||||||
fds: Vec<OwnedFd>,
|
|
||||||
) -> Result<(), ScenegraphError> {
|
|
||||||
let Some(client) = self.get_client() else {
|
|
||||||
return Err(ScenegraphError::SignalNotFound);
|
|
||||||
};
|
|
||||||
debug_span!("Handle signal", path, method).in_scope(|| {
|
debug_span!("Handle signal", path, method).in_scope(|| {
|
||||||
self.get_node(path)
|
self.get_node(path)
|
||||||
.ok_or(ScenegraphError::NodeNotFound)?
|
.ok_or(ScenegraphError::NodeNotFound)?
|
||||||
.send_local_signal(
|
.send_local_signal(client, method, data)
|
||||||
client,
|
|
||||||
method,
|
|
||||||
Message {
|
|
||||||
data: data.to_vec(),
|
|
||||||
fds,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn execute_method(
|
fn execute_method(
|
||||||
@@ -92,26 +63,12 @@ impl scenegraph::Scenegraph for Scenegraph {
|
|||||||
path: &str,
|
path: &str,
|
||||||
method: &str,
|
method: &str,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
fds: Vec<OwnedFd>,
|
) -> Result<Vec<u8>, ScenegraphError> {
|
||||||
response: oneshot::Sender<Result<(Vec<u8>, Vec<OwnedFd>), ScenegraphError>>,
|
let Some(client) = self.get_client() else {return Err(ScenegraphError::MethodNotFound)};
|
||||||
) {
|
debug_span!("Handle method", path, method).in_scope(|| {
|
||||||
let Some(client) = self.get_client() else {
|
self.get_node(path)
|
||||||
let _ = response.send(Err(ScenegraphError::MethodNotFound));
|
.ok_or(ScenegraphError::NodeNotFound)?
|
||||||
return;
|
.execute_local_method(client, method, data)
|
||||||
};
|
})
|
||||||
debug!(path, method, "Handle method");
|
|
||||||
let Some(node) = self.get_node(path) else {
|
|
||||||
let _ = response.send(Err(ScenegraphError::NodeNotFound));
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
node.execute_local_method(
|
|
||||||
client,
|
|
||||||
method,
|
|
||||||
Message {
|
|
||||||
data: data.to_vec(),
|
|
||||||
fds,
|
|
||||||
},
|
|
||||||
MethodResponseSender(response),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn new<
|
pub fn new<
|
||||||
F: FnOnce() -> S,
|
F: FnOnce() -> S,
|
||||||
S: AsRef<str>,
|
S: AsRef<str>,
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
202
src/main.rs
202
src/main.rs
@@ -4,26 +4,21 @@ mod objects;
|
|||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
mod wayland;
|
mod wayland;
|
||||||
|
|
||||||
use crate::core::client::CLIENTS;
|
use crate::core::destroy_queue;
|
||||||
use crate::core::client_state::ClientState;
|
|
||||||
use crate::core::{destroy_queue, buffers};
|
|
||||||
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;
|
||||||
use crate::objects::input::mouse_pointer::MousePointer;
|
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 self::core::eventloop::EventLoop;
|
use self::core::eventloop::EventLoop;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
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::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::Command;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
@@ -31,10 +26,7 @@ use stereokit::{
|
|||||||
TextureFormat, TextureType,
|
TextureFormat, TextureType,
|
||||||
};
|
};
|
||||||
use stereokit::{DisplayBlend, Sk};
|
use stereokit::{DisplayBlend, Sk};
|
||||||
use tokio::sync::Notify;
|
|
||||||
use tokio::task::LocalSet;
|
|
||||||
use tokio::{runtime::Handle, sync::oneshot};
|
use tokio::{runtime::Handle, sync::oneshot};
|
||||||
use tracing::metadata::LevelFilter;
|
|
||||||
use tracing::{debug_span, error, info};
|
use tracing::{debug_span, error, info};
|
||||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||||
|
|
||||||
@@ -60,25 +52,20 @@ struct CliArgs {
|
|||||||
|
|
||||||
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
|
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
|
||||||
static SK_MULTITHREAD: OnceCell<Sk> = OnceCell::new();
|
static SK_MULTITHREAD: OnceCell<Sk> = OnceCell::new();
|
||||||
static STOP_NOTIFIER: Notify = Notify::const_new();
|
|
||||||
|
|
||||||
struct EventLoopInfo {
|
struct EventLoopInfo {
|
||||||
tokio_handle: Handle,
|
tokio_handle: Handle,
|
||||||
socket_path: PathBuf,
|
socket_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<()> {
|
||||||
ctrlc::set_handler(|| {
|
|
||||||
if atty::isnt(atty::Stream::Stdout) {
|
|
||||||
STOP_NOTIFIER.notify_waiters()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let registry = tracing_subscriber::registry();
|
let registry = tracing_subscriber::registry();
|
||||||
|
|
||||||
#[cfg(feature = "profile_app")]
|
#[cfg(feature = "profile_app")]
|
||||||
let registry = registry.with(tracing_tracy::TracyLayer::new().with_filter(LevelFilter::DEBUG));
|
let (chrome_layer, _guard) = tracing_chrome::ChromeLayerBuilder::new()
|
||||||
|
.include_args(true)
|
||||||
|
.build();
|
||||||
|
#[cfg(feature = "profile_app")]
|
||||||
|
let registry = registry.with(chrome_layer);
|
||||||
|
|
||||||
#[cfg(feature = "profile_tokio")]
|
#[cfg(feature = "profile_tokio")]
|
||||||
let (console_layer, _) = console_subscriber::ConsoleLayer::builder().build();
|
let (console_layer, _) = console_subscriber::ConsoleLayer::builder().build();
|
||||||
@@ -107,15 +94,7 @@ fn main() {
|
|||||||
},
|
},
|
||||||
blend_preference: DisplayBlend::AnyTransparent,
|
blend_preference: DisplayBlend::AnyTransparent,
|
||||||
depth_mode: DepthMode::D32,
|
depth_mode: DepthMode::D32,
|
||||||
log_filter: match EnvFilter::from_default_env().max_level_hint() {
|
log_filter: LogLevel::None,
|
||||||
Some(LevelFilter::ERROR) => LogLevel::Error,
|
|
||||||
Some(LevelFilter::WARN) => LogLevel::Warning,
|
|
||||||
Some(LevelFilter::INFO) => LogLevel::Inform,
|
|
||||||
Some(LevelFilter::DEBUG) => LogLevel::Diagnostic,
|
|
||||||
Some(LevelFilter::TRACE) => LogLevel::Diagnostic,
|
|
||||||
Some(LevelFilter::OFF) => LogLevel::None,
|
|
||||||
None => LogLevel::Warning,
|
|
||||||
},
|
|
||||||
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,
|
||||||
@@ -127,8 +106,8 @@ fn main() {
|
|||||||
info!("Init StereoKit");
|
info!("Init StereoKit");
|
||||||
|
|
||||||
sk.material_set_shader(
|
sk.material_set_shader(
|
||||||
sk.material_find("default/material_pbr").unwrap(),
|
sk.material_find("default/material_pbr")?,
|
||||||
sk.shader_find("default/shader_pbr_clip").unwrap(),
|
sk.shader_find("default/shader_pbr_clip")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Skytex/light stuff
|
// Skytex/light stuff
|
||||||
@@ -156,11 +135,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mouse_pointer = cli_args
|
let mouse_pointer = cli_args.flatscreen.then(MousePointer::new).transpose()?;
|
||||||
.flatscreen
|
|
||||||
.then(MousePointer::new)
|
|
||||||
.transpose()
|
|
||||||
.unwrap();
|
|
||||||
let mut hands = (!cli_args.flatscreen)
|
let mut hands = (!cli_args.flatscreen)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let left = SkHand::new(Handed::Left).ok();
|
let left = SkHand::new(Handed::Left).ok();
|
||||||
@@ -170,89 +145,58 @@ fn main() {
|
|||||||
.flatten();
|
.flatten();
|
||||||
let mut controllers = (!cli_args.flatscreen && !cli_args.disable_controller)
|
let mut controllers = (!cli_args.flatscreen && !cli_args.disable_controller)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let left = SkController::new(&sk, Handed::Left).ok();
|
let left = SkController::new(Handed::Left).ok();
|
||||||
let right = SkController::new(&sk, Handed::Right).ok();
|
let right = SkController::new(Handed::Right).ok();
|
||||||
left.zip(right)
|
left.zip(right)
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
let eye_pointer = (sk.active_display_mode() == DisplayMode::MixedReality
|
let eye_pointer = (!cli_args.flatscreen && sk.device_has_eye_gaze())
|
||||||
&& sk.device_has_eye_gaze())
|
.then(EyePointer::new)
|
||||||
.then(EyePointer::new)
|
.transpose()?;
|
||||||
.transpose()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if hands.is_none() {
|
if hands.is_none() {
|
||||||
sk.input_hand_visible(Handed::Left, false);
|
sk.input_hand_visible(Handed::Left, false);
|
||||||
sk.input_hand_visible(Handed::Right, false);
|
sk.input_hand_visible(Handed::Right, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let play_space = sk
|
let (event_stop_tx, event_stop_rx) = oneshot::channel::<()>();
|
||||||
.world_has_bounds()
|
|
||||||
.then(|| PlaySpace::new().ok())
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
let (info_sender, info_receiver) = oneshot::channel::<EventLoopInfo>();
|
let (info_sender, info_receiver) = oneshot::channel::<EventLoopInfo>();
|
||||||
let event_thread = std::thread::Builder::new()
|
let event_thread = std::thread::Builder::new()
|
||||||
.name("event_loop".to_owned())
|
.name("event_loop".to_owned())
|
||||||
.spawn(move || event_loop(info_sender))
|
.spawn(move || event_loop(info_sender, event_stop_rx))?;
|
||||||
.unwrap();
|
let event_loop_info = info_receiver.blocking_recv()?;
|
||||||
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()?;
|
||||||
info!("Stardust ready!");
|
info!("Stardust ready!");
|
||||||
|
|
||||||
let mut startup_child = (|| {
|
if let Some(project_dirs) = project_dirs.as_ref() {
|
||||||
let project_dirs = project_dirs.as_ref()?;
|
|
||||||
let startup_script_path = cli_args
|
let startup_script_path = cli_args
|
||||||
.startup_script
|
.startup_script
|
||||||
.clone()
|
.clone()
|
||||||
.and_then(|p| p.canonicalize().ok())
|
.and_then(|p| p.canonicalize().ok())
|
||||||
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
||||||
let mut startup_command = Command::new("bash");
|
let _startup = Command::new(startup_script_path)
|
||||||
startup_command.arg(startup_script_path);
|
.env(
|
||||||
startup_command.arg("&");
|
"FLAT_WAYLAND_DISPLAY",
|
||||||
|
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
||||||
startup_command.stdin(Stdio::null());
|
)
|
||||||
startup_command.stdout(Stdio::null());
|
.env("WAYLAND_DISPLAY", &wayland.socket_name)
|
||||||
startup_command.stderr(Stdio::null());
|
.env(
|
||||||
startup_command.env(
|
"STARDUST_INSTANCE",
|
||||||
"FLAT_WAYLAND_DISPLAY",
|
event_loop_info
|
||||||
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
.socket_path
|
||||||
);
|
.file_name()
|
||||||
startup_command.env(
|
.expect("Stardust socket path not found"),
|
||||||
"STARDUST_INSTANCE",
|
)
|
||||||
event_loop_info
|
.env("GDK_BACKEND", "wayland")
|
||||||
.socket_path
|
.env("QT_QPA_PLATFORM", "wayland")
|
||||||
.file_name()
|
.env("MOZ_ENABLE_WAYLAND", "1")
|
||||||
.expect("Stardust socket path not found"),
|
.env("CLUTTER_BACKEND", "wayland")
|
||||||
);
|
.env("SDL_VIDEODRIVER", "wayland")
|
||||||
#[cfg(feature = "wayland")]
|
.spawn();
|
||||||
{
|
}
|
||||||
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
|
||||||
startup_command.env("WAYLAND_DISPLAY", &wayland_socket);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
startup_command.env("DISPLAY", format!(":{}", wayland.xwayland_state.display));
|
|
||||||
startup_command.env("GDK_BACKEND", "wayland");
|
|
||||||
startup_command.env("QT_QPA_PLATFORM", "wayland");
|
|
||||||
startup_command.env("MOZ_ENABLE_WAYLAND", "1");
|
|
||||||
startup_command.env("CLUTTER_BACKEND", "wayland");
|
|
||||||
startup_command.env("SDL_VIDEODRIVER", "wayland");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
startup_command.pre_exec(|| {
|
|
||||||
nix::unistd::setsid()
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|_| std::io::ErrorKind::Other.into())
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let child = startup_command.spawn().ok()?;
|
|
||||||
Some(child)
|
|
||||||
})();
|
|
||||||
|
|
||||||
let mut last_frame_delta = Duration::ZERO;
|
let mut last_frame_delta = Duration::ZERO;
|
||||||
let mut sleep_duration = Duration::ZERO;
|
let mut sleep_duration = Duration::ZERO;
|
||||||
@@ -262,18 +206,17 @@ 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);
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
wayland.frame_event(sk);
|
wayland.frame_event(sk);
|
||||||
destroy_queue::clear();
|
destroy_queue::clear();
|
||||||
|
|
||||||
if let Some(mouse_pointer) = &mut mouse_pointer {
|
if let Some(mouse_pointer) = &mouse_pointer {
|
||||||
mouse_pointer.update(sk);
|
mouse_pointer.update(sk);
|
||||||
}
|
}
|
||||||
if let Some((left_hand, right_hand)) = &mut hands {
|
if let Some((left_hand, right_hand)) = &mut hands {
|
||||||
left_hand.update(!cli_args.disable_controller, sk);
|
left_hand.update(sk);
|
||||||
right_hand.update(!cli_args.disable_controller, sk);
|
right_hand.update(sk);
|
||||||
}
|
}
|
||||||
if let Some((left_controller, right_controller)) = &mut controllers {
|
if let Some((left_controller, right_controller)) = &mut controllers {
|
||||||
left_controller.update(sk);
|
left_controller.update(sk);
|
||||||
@@ -282,9 +225,6 @@ fn main() {
|
|||||||
if let Some(eye_pointer) = &eye_pointer {
|
if let Some(eye_pointer) = &eye_pointer {
|
||||||
eye_pointer.update(sk);
|
eye_pointer.update(sk);
|
||||||
}
|
}
|
||||||
if let Some(play_space) = &play_space {
|
|
||||||
play_space.update(sk);
|
|
||||||
}
|
|
||||||
input::process_input();
|
input::process_input();
|
||||||
nodes::root::Root::send_frame_events(sk.time_elapsed_unscaled());
|
nodes::root::Root::send_frame_events(sk.time_elapsed_unscaled());
|
||||||
adaptive_sleep(
|
adaptive_sleep(
|
||||||
@@ -295,20 +235,14 @@ 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| {
|
|_| {
|
||||||
info!("Cleanly shut down StereoKit");
|
info!("Cleanly shut down StereoKit");
|
||||||
|
|
||||||
if let Some(mut startup_child) = startup_child.take() {
|
|
||||||
let _ = startup_child.kill();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -316,13 +250,12 @@ fn main() {
|
|||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
drop(wayland);
|
drop(wayland);
|
||||||
|
|
||||||
STOP_NOTIFIER.notify_waiters();
|
let _ = event_stop_tx.send(());
|
||||||
event_thread
|
event_thread
|
||||||
.join()
|
.join()
|
||||||
.expect("Failed to cleanly shut down event loop")
|
.expect("Failed to cleanly shut down event loop")?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
info!("Cleanly shut down Stardust");
|
info!("Cleanly shut down Stardust");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adaptive_sleep(
|
fn adaptive_sleep(
|
||||||
@@ -350,7 +283,10 @@ fn adaptive_sleep(
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
// #[tokio::main(flavor = "current_thread")]
|
// #[tokio::main(flavor = "current_thread")]
|
||||||
async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::eyre::Result<()> {
|
async fn event_loop(
|
||||||
|
info_sender: oneshot::Sender<EventLoopInfo>,
|
||||||
|
stop_rx: oneshot::Receiver<()>,
|
||||||
|
) -> color_eyre::eyre::Result<()> {
|
||||||
let socket_path =
|
let socket_path =
|
||||||
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
||||||
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
||||||
@@ -365,9 +301,11 @@ async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::
|
|||||||
socket_path,
|
socket_path,
|
||||||
});
|
});
|
||||||
|
|
||||||
STOP_NOTIFIER.notified().await;
|
let result = tokio::select! {
|
||||||
println!("Stopping...");
|
biased;
|
||||||
save_clients().await;
|
_ = tokio::signal::ctrl_c() => Ok(()),
|
||||||
|
_ = stop_rx => Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
info!("Cleanly shut down event loop");
|
info!("Cleanly shut down event loop");
|
||||||
|
|
||||||
@@ -375,19 +313,5 @@ async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::
|
|||||||
stereokit::sys::sk_quit();
|
stereokit::sys::sk_quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
result
|
||||||
}
|
|
||||||
|
|
||||||
async fn save_clients() {
|
|
||||||
let local_set = LocalSet::new();
|
|
||||||
for client in CLIENTS.get_vec() {
|
|
||||||
local_set.spawn_local(async move {
|
|
||||||
tokio::select! {
|
|
||||||
biased;
|
|
||||||
s = client.save_state() => {s.map(ClientState::to_file);},
|
|
||||||
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
local_set.await;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::Node;
|
use super::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 std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
@@ -13,7 +12,6 @@ pub struct AliasInfo {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Alias {
|
pub struct Alias {
|
||||||
pub enabled: Arc<AtomicBool>,
|
|
||||||
pub(super) node: Weak<Node>,
|
pub(super) node: Weak<Node>,
|
||||||
pub original: Weak<Node>,
|
pub original: Weak<Node>,
|
||||||
|
|
||||||
@@ -37,7 +35,6 @@ impl Alias {
|
|||||||
|
|
||||||
let node = Node::create(client, parent, name, true).add_to_scenegraph()?;
|
let node = Node::create(client, parent, name, true).add_to_scenegraph()?;
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
enabled: Arc::new(AtomicBool::new(true)),
|
|
||||||
node: Arc::downgrade(&node),
|
node: Arc::downgrade(&node),
|
||||||
original: Arc::downgrade(original),
|
original: Arc::downgrade(original),
|
||||||
info,
|
info,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{Message, Node};
|
use super::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;
|
||||||
@@ -83,13 +83,13 @@ impl Sound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_flex(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
fn play_flex(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
|
||||||
let sound = node.sound.get().unwrap();
|
let sound = node.sound.get().unwrap();
|
||||||
sound.play.lock().replace(());
|
sound.play.lock().replace(());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_flex(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
pub fn stop_flex(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
|
||||||
let sound = node.sound.get().unwrap();
|
let sound = node.sound.get().unwrap();
|
||||||
sound.stop.lock().replace(());
|
sound.stop.lock().replace(());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -108,7 +108,7 @@ pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|||||||
node.add_to_scenegraph().map(|_| ())
|
node.add_to_scenegraph().map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateSoundInfo<'a> {
|
struct CreateSoundInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -116,7 +116,7 @@ pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message)
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
resource: ResourceID,
|
resource: ResourceID,
|
||||||
}
|
}
|
||||||
let info: CreateSoundInfo = deserialize(message.as_ref())?;
|
let info: CreateSoundInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/audio/sound", info.name, true);
|
let node = Node::create(&calling_client, "/audio/sound", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = parse_transform(info.transform, true, true, true);
|
||||||
|
|||||||
@@ -1,30 +1,21 @@
|
|||||||
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, 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::nodes::fields::{find_field, FIELD_ALIAS_INFO};
|
use crate::nodes::fields::{find_field, FIELD_ALIAS_INFO};
|
||||||
use crate::nodes::spatial::find_spatial_parent;
|
use crate::nodes::spatial::find_spatial_parent;
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
use color_eyre::eyre::{ensure, eyre, Result};
|
||||||
use glam::vec3a;
|
use glam::vec3a;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use mint::{Quaternion, Vector3};
|
use mint::{Quaternion, Vector3};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize};
|
use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize};
|
||||||
use stardust_xr::values::Transform;
|
use stardust_xr::values::Transform;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref KEYMAPS: Mutex<FxHashMap<String, String>> = Mutex::new(FxHashMap::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -33,16 +24,7 @@ pub fn mask_matches(mask_map_lesser: &Mask, mask_map_greater: &Mask) -> bool {
|
|||||||
for key in mask_map_lesser.get_mask()?.iter_keys() {
|
for key in mask_map_lesser.get_mask()?.iter_keys() {
|
||||||
let lesser_key = mask_map_lesser.get_mask()?.index(key)?;
|
let lesser_key = mask_map_lesser.get_mask()?.index(key)?;
|
||||||
let greater_key = mask_map_greater.get_mask()?.index(key)?;
|
let greater_key = mask_map_greater.get_mask()?.index(key)?;
|
||||||
// otherwise zero-length vectors don't count the same as a single type vector
|
if lesser_key.to_string() != greater_key.to_string() {
|
||||||
if lesser_key.flexbuffer_type().is_heterogenous_vector()
|
|
||||||
&& lesser_key.as_vector().len() == 0
|
|
||||||
&& greater_key.flexbuffer_type().is_vector()
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !lesser_key.flexbuffer_type().is_null()
|
|
||||||
&& lesser_key.flexbuffer_type() != greater_key.flexbuffer_type()
|
|
||||||
{
|
|
||||||
return Err(flexbuffers::ReaderError::InvalidPackedType {}.into());
|
return Err(flexbuffers::ReaderError::InvalidPackedType {}.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,11 +35,6 @@ pub fn mask_matches(mask_map_lesser: &Mask, mask_map_greater: &Mask) -> bool {
|
|||||||
|
|
||||||
pub struct Mask(pub Vec<u8>);
|
pub struct Mask(pub Vec<u8>);
|
||||||
impl Mask {
|
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]>> {
|
pub fn get_mask(&self) -> Result<flexbuffers::MapReader<&[u8]>> {
|
||||||
flexbuffers::Reader::get_root(self.0.as_slice())
|
flexbuffers::Reader::get_root(self.0.as_slice())
|
||||||
.map_err(|_| eyre!("Mask is not a valid flexbuffer"))?
|
.map_err(|_| eyre!("Mask is not a valid flexbuffer"))?
|
||||||
@@ -65,14 +42,6 @@ impl Mask {
|
|||||||
.map_err(|_| eyre!("Mask is not a valid 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)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SendDataInfo<'a> {
|
struct SendDataInfo<'a> {
|
||||||
@@ -111,9 +80,9 @@ 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 { return };
|
||||||
let Some(tx_client) = tx_node.get_client() else {return};
|
let Some(tx_client) = tx_node.get_client() else { return };
|
||||||
let Some(rx_node) = receiver.node.upgrade() else {return};
|
let Some(rx_node) = receiver.node.upgrade() else { return };
|
||||||
// Receiver itself
|
// Receiver itself
|
||||||
let rx_alias = Alias::create(
|
let rx_alias = Alias::create(
|
||||||
&tx_client,
|
&tx_client,
|
||||||
@@ -168,7 +137,7 @@ impl PulseSender {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Ok(data) = serialize(info) else {return};
|
let Ok(data) = serialize(info) else {return};
|
||||||
let _ = tx_node.send_remote_signal("new_receiver", data);
|
let _ = tx_node.send_remote_signal("new_receiver", &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_drop_receiver(&self, receiver: &PulseReceiver) {
|
fn handle_drop_receiver(&self, receiver: &PulseReceiver) {
|
||||||
@@ -177,11 +146,11 @@ impl PulseSender {
|
|||||||
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 {return};
|
||||||
let Ok(data) = serialize(&uid) else {return};
|
let Ok(data) = serialize(&uid) else {return};
|
||||||
let _ = tx_node.send_remote_signal("drop_receiver", data);
|
let _ = tx_node.send_remote_signal("drop_receiver", &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_data_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn send_data_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
let info: SendDataInfo = deserialize(message.as_ref())?;
|
let info: SendDataInfo = deserialize(data)?;
|
||||||
let receiver_node = calling_client.get_node("Pulse receiver", info.uid)?;
|
let receiver_node = calling_client.get_node("Pulse receiver", info.uid)?;
|
||||||
let receiver =
|
let receiver =
|
||||||
receiver_node.get_aspect("Pulse Receiver", "pulse receiver", |n| &n.pulse_receiver)?;
|
receiver_node.get_aspect("Pulse Receiver", "pulse receiver", |n| &n.pulse_receiver)?;
|
||||||
@@ -195,9 +164,7 @@ impl PulseSender {
|
|||||||
data_mask.get_mask()?;
|
data_mask.get_mask()?;
|
||||||
ensure!(
|
ensure!(
|
||||||
mask_matches(receiver_mask, &data_mask),
|
mask_matches(receiver_mask, &data_mask),
|
||||||
"Message ({}) does not contain the same keys as the receiver's 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)
|
receiver.send_data(&node.pulse_sender.get().unwrap().uid, data_mask.0)
|
||||||
}
|
}
|
||||||
@@ -215,7 +182,7 @@ pub struct PulseReceiver {
|
|||||||
pub mask: Mask,
|
pub mask: Mask,
|
||||||
}
|
}
|
||||||
impl PulseReceiver {
|
impl PulseReceiver {
|
||||||
pub fn add_to(node: &Arc<Node>, field: Arc<Field>, mask: Mask) -> Result<Arc<PulseReceiver>> {
|
pub fn add_to(node: &Arc<Node>, field: Arc<Field>, mask: Mask) -> Result<()> {
|
||||||
ensure!(
|
ensure!(
|
||||||
node.spatial.get().is_some(),
|
node.spatial.get().is_some(),
|
||||||
"Internal: Node does not have a spatial attached!"
|
"Internal: Node does not have a spatial attached!"
|
||||||
@@ -232,13 +199,13 @@ impl PulseReceiver {
|
|||||||
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());
|
let _ = node.pulse_receiver.set(receiver);
|
||||||
Ok(receiver)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_data(&self, uid: &str, data: Vec<u8>) -> Result<()> {
|
pub fn send_data(&self, uid: &str, data: Vec<u8>) -> Result<()> {
|
||||||
if let Some(node) = self.node.upgrade() {
|
if let Some(node) = self.node.upgrade() {
|
||||||
node.send_remote_signal("data", serialize(SendDataInfo { uid, data })?)?;
|
node.send_remote_signal("data", &serialize(SendDataInfo { uid, data })?)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -257,15 +224,13 @@ pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|||||||
let node = Node::create(client, "", "data", false);
|
let node = Node::create(client, "", "data", false);
|
||||||
node.add_local_signal("create_pulse_sender", create_pulse_sender_flex);
|
node.add_local_signal("create_pulse_sender", create_pulse_sender_flex);
|
||||||
node.add_local_signal("create_pulse_receiver", create_pulse_receiver_flex);
|
node.add_local_signal("create_pulse_receiver", create_pulse_receiver_flex);
|
||||||
node.add_local_method("register_keymap", register_keymap_flex);
|
|
||||||
node.add_local_method("get_keymap", get_keymap_flex);
|
|
||||||
node.add_to_scenegraph().map(|_| ())
|
node.add_to_scenegraph().map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_pulse_sender_flex(
|
pub fn create_pulse_sender_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreatePulseSenderInfo<'a> {
|
struct CreatePulseSenderInfo<'a> {
|
||||||
@@ -274,7 +239,7 @@ pub fn create_pulse_sender_flex(
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
mask: Vec<u8>,
|
mask: Vec<u8>,
|
||||||
}
|
}
|
||||||
let info: CreatePulseSenderInfo = deserialize(message.as_ref())?;
|
let info: CreatePulseSenderInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/data/sender", info.name, true);
|
let node = Node::create(&calling_client, "/data/sender", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
@@ -291,7 +256,7 @@ pub fn create_pulse_sender_flex(
|
|||||||
pub fn create_pulse_receiver_flex(
|
pub fn create_pulse_receiver_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreatePulseReceiverInfo<'a> {
|
struct CreatePulseReceiverInfo<'a> {
|
||||||
@@ -301,7 +266,7 @@ pub fn create_pulse_receiver_flex(
|
|||||||
field_path: &'a str,
|
field_path: &'a str,
|
||||||
mask: Vec<u8>,
|
mask: Vec<u8>,
|
||||||
}
|
}
|
||||||
let info: CreatePulseReceiverInfo = deserialize(message.as_ref())?;
|
let info: CreatePulseReceiverInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/data/receiver", info.name, true);
|
let node = Node::create(&calling_client, "/data/receiver", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
@@ -314,42 +279,3 @@ pub fn create_pulse_receiver_flex(
|
|||||||
PulseReceiver::add_to(&node, field, mask)?;
|
PulseReceiver::add_to(&node, field, mask)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
if let Some(found_keymap_id) = keymaps
|
|
||||||
.iter()
|
|
||||||
.filter(|(_k, v)| *v == &keymap)
|
|
||||||
.map(|(k, _v)| k)
|
|
||||||
.last()
|
|
||||||
{
|
|
||||||
return Ok(serialize(found_keymap_id)?.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let generated_id = nanoid!();
|
|
||||||
keymaps.insert(generated_id.clone(), keymap);
|
|
||||||
|
|
||||||
Ok(serialize(generated_id)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
core::{client::Client, registry::Registry},
|
core::{client::Client, registry::Registry},
|
||||||
nodes::{
|
nodes::{
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
spatial::{find_spatial_parent, parse_transform, Spatial},
|
||||||
Message, Node,
|
Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, ensure, Result};
|
use color_eyre::eyre::{bail, ensure, Result};
|
||||||
@@ -97,14 +97,10 @@ impl Lines {
|
|||||||
draw_ctx.line_add_listv(points.make_contiguous());
|
draw_ctx.line_add_listv(points.make_contiguous());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_points_flex(
|
pub fn set_points_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(Drawable::Lines(lines)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let Some(Drawable::Lines(lines)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||||
|
|
||||||
let mut points: Vec<LinePointRaw> = deserialize(message.as_ref())?;
|
let mut points: Vec<LinePointRaw> = deserialize(data)?;
|
||||||
for p in &mut points {
|
for p in &mut points {
|
||||||
p.color[0] = p.color[0].powf(2.2);
|
p.color[0] = p.color[0].powf(2.2);
|
||||||
p.color[1] = p.color[1].powf(2.2);
|
p.color[1] = p.color[1].powf(2.2);
|
||||||
@@ -113,14 +109,10 @@ impl Lines {
|
|||||||
lines.data.lock().points = points;
|
lines.data.lock().points = points;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn set_cyclic_flex(
|
pub fn set_cyclic_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(Drawable::Lines(lines)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let Some(Drawable::Lines(lines)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||||
|
|
||||||
lines.data.lock().cyclic = deserialize(message.as_ref())?;
|
lines.data.lock().cyclic = deserialize(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +130,7 @@ pub fn draw_all(draw_ctx: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateTextInfo<'a> {
|
struct CreateTextInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -147,7 +139,7 @@ pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message)
|
|||||||
points: Vec<LinePointRaw>,
|
points: Vec<LinePointRaw>,
|
||||||
cyclic: bool,
|
cyclic: bool,
|
||||||
}
|
}
|
||||||
let mut info: CreateTextInfo = deserialize(message.as_ref())?;
|
let mut info: CreateTextInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/drawable/lines", info.name, true);
|
let node = Node::create(&calling_client, "/drawable/lines", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = parse_transform(info.transform, true, true, true);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
pub mod lines;
|
pub mod lines;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod shaders;
|
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
@@ -9,7 +8,7 @@ use self::{
|
|||||||
text::Text,
|
text::Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Message, Node};
|
use super::Node;
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@@ -56,14 +55,14 @@ 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<()> {
|
fn set_sky_file_flex(_node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct SkyFileInfo {
|
struct SkyFileInfo {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
skytex: Option<bool>,
|
skytex: Option<bool>,
|
||||||
skylight: Option<bool>,
|
skylight: Option<bool>,
|
||||||
}
|
}
|
||||||
let info: SkyFileInfo = deserialize(message.as_ref())?;
|
let info: SkyFileInfo = deserialize(data)?;
|
||||||
info.path.metadata()?;
|
info.path.metadata()?;
|
||||||
if info.skytex.unwrap_or_default() {
|
if info.skytex.unwrap_or_default() {
|
||||||
QUEUED_SKYTEX.lock().replace(info.path.clone());
|
QUEUED_SKYTEX.lock().replace(info.path.clone());
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::core::registry::Registry;
|
|||||||
use crate::core::resource::ResourceID;
|
use crate::core::resource::ResourceID;
|
||||||
use crate::nodes::drawable::Drawable;
|
use crate::nodes::drawable::Drawable;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use crate::nodes::Message;
|
|
||||||
use crate::SK_MULTITHREAD;
|
use crate::SK_MULTITHREAD;
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
@@ -14,6 +13,7 @@ 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 send_wrapper::SendWrapper;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::schemas::flex::deserialize;
|
use stardust_xr::schemas::flex::deserialize;
|
||||||
use stardust_xr::values::Transform;
|
use stardust_xr::values::Transform;
|
||||||
@@ -121,7 +121,7 @@ pub struct ModelPart {
|
|||||||
space: Arc<Spatial>,
|
space: Arc<Spatial>,
|
||||||
model: Weak<Model>,
|
model: Weak<Model>,
|
||||||
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
|
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
|
||||||
pending_material_replacement: Mutex<Option<Arc<Material>>>,
|
pending_material_replacement: Mutex<Option<Arc<SendWrapper<Material>>>>,
|
||||||
}
|
}
|
||||||
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) {
|
||||||
@@ -213,11 +213,11 @@ impl ModelPart {
|
|||||||
fn set_material_parameter_flex(
|
fn set_material_parameter_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||||
|
|
||||||
let (name, value): (String, MaterialParameter) = deserialize(message.as_ref())?;
|
let (name, value): (String, MaterialParameter) = deserialize(data)?;
|
||||||
|
|
||||||
model_part
|
model_part
|
||||||
.pending_material_parameters
|
.pending_material_parameters
|
||||||
@@ -227,7 +227,7 @@ impl ModelPart {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_material(&self, replacement: Arc<Material>) {
|
pub fn replace_material(&self, replacement: Arc<SendWrapper<Material>>) {
|
||||||
self.pending_material_replacement
|
self.pending_material_replacement
|
||||||
.lock()
|
.lock()
|
||||||
.replace(replacement);
|
.replace(replacement);
|
||||||
@@ -342,7 +342,7 @@ pub fn draw_all(sk: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateModelInfo<'a> {
|
struct CreateModelInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -350,7 +350,7 @@ pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message)
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
resource: ResourceID,
|
resource: ResourceID,
|
||||||
}
|
}
|
||||||
let info: CreateModelInfo = deserialize(message.as_ref())?;
|
let info: CreateModelInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/drawable/model", info.name, true);
|
let node = Node::create(&calling_client, "/drawable/model", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = parse_transform(info.transform, true, true, true);
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use smithay::backend::renderer::gles::{
|
|
||||||
ffi::{self, Gles2, FRAGMENT_SHADER, VERTEX_SHADER},
|
|
||||||
GlesError,
|
|
||||||
};
|
|
||||||
use std::mem::transmute;
|
|
||||||
use stereokit::Shader;
|
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
// Simula shader with fancy lanzcos sampling
|
|
||||||
pub const UNLIT_SHADER_BYTES: &[u8] = include_bytes!("shader_unlit_gamma.sks");
|
|
||||||
|
|
||||||
// Simula shader with fancy lanzcos sampling
|
|
||||||
pub const PANEL_SHADER_BYTES: &[u8] = include_bytes!("shader_unlit_simula.sks");
|
|
||||||
|
|
||||||
struct FfiAssetHeader {
|
|
||||||
asset_type: i32,
|
|
||||||
asset_state: i32,
|
|
||||||
id: u64,
|
|
||||||
index: u64,
|
|
||||||
refs: i32,
|
|
||||||
debug: *mut u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FfiSkgShader {
|
|
||||||
meta: *mut u8,
|
|
||||||
vertex: u32,
|
|
||||||
pixel: u32,
|
|
||||||
program: u32,
|
|
||||||
compute: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FfiShader {
|
|
||||||
header: FfiAssetHeader,
|
|
||||||
shader: FfiSkgShader,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn compile_shader(
|
|
||||||
gl: &ffi::Gles2,
|
|
||||||
variant: ffi::types::GLuint,
|
|
||||||
src: &str,
|
|
||||||
) -> Result<ffi::types::GLuint, GlesError> {
|
|
||||||
let shader = gl.CreateShader(variant);
|
|
||||||
if shader == 0 {
|
|
||||||
return Err(GlesError::CreateShaderObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.ShaderSource(
|
|
||||||
shader,
|
|
||||||
1,
|
|
||||||
&src.as_ptr() as *const *const u8 as *const *const ffi::types::GLchar,
|
|
||||||
&(src.len() as i32) as *const _,
|
|
||||||
);
|
|
||||||
gl.CompileShader(shader);
|
|
||||||
|
|
||||||
let mut status = ffi::FALSE as i32;
|
|
||||||
gl.GetShaderiv(shader, ffi::COMPILE_STATUS, &mut status as *mut _);
|
|
||||||
if status == ffi::FALSE as i32 {
|
|
||||||
let mut max_len = 0;
|
|
||||||
gl.GetShaderiv(shader, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _);
|
|
||||||
|
|
||||||
let mut error = Vec::with_capacity(max_len as usize);
|
|
||||||
let mut len = 0;
|
|
||||||
gl.GetShaderInfoLog(
|
|
||||||
shader,
|
|
||||||
max_len as _,
|
|
||||||
&mut len as *mut _,
|
|
||||||
error.as_mut_ptr() as *mut _,
|
|
||||||
);
|
|
||||||
error.set_len(len as usize);
|
|
||||||
|
|
||||||
error!(
|
|
||||||
"[GL] {}",
|
|
||||||
std::str::from_utf8(&error).unwrap_or("<Error Message no utf8>")
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.DeleteShader(shader);
|
|
||||||
return Err(GlesError::ShaderCompileError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(shader)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn link_program(
|
|
||||||
gl: &ffi::Gles2,
|
|
||||||
vert: ffi::types::GLuint,
|
|
||||||
frag: ffi::types::GLuint,
|
|
||||||
) -> Result<ffi::types::GLuint, GlesError> {
|
|
||||||
let program = gl.CreateProgram();
|
|
||||||
gl.AttachShader(program, vert);
|
|
||||||
gl.AttachShader(program, frag);
|
|
||||||
gl.LinkProgram(program);
|
|
||||||
// gl.DetachShader(program, vert);
|
|
||||||
// gl.DetachShader(program, frag);
|
|
||||||
// gl.DeleteShader(vert);
|
|
||||||
// gl.DeleteShader(frag);
|
|
||||||
|
|
||||||
let mut status = ffi::FALSE as i32;
|
|
||||||
gl.GetProgramiv(program, ffi::LINK_STATUS, &mut status as *mut _);
|
|
||||||
if status == ffi::FALSE as i32 {
|
|
||||||
let mut max_len = 0;
|
|
||||||
gl.GetProgramiv(program, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _);
|
|
||||||
|
|
||||||
let mut error = Vec::with_capacity(max_len as usize);
|
|
||||||
let mut len = 0;
|
|
||||||
gl.GetProgramInfoLog(
|
|
||||||
program,
|
|
||||||
max_len as _,
|
|
||||||
&mut len as *mut _,
|
|
||||||
error.as_mut_ptr() as *mut _,
|
|
||||||
);
|
|
||||||
error.set_len(len as usize);
|
|
||||||
|
|
||||||
error!(
|
|
||||||
"[GL] {}",
|
|
||||||
std::str::from_utf8(&error).unwrap_or("<Error Message no utf8>")
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.DeleteProgram(program);
|
|
||||||
return Err(GlesError::ProgramLinkError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(program)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn shader_inject(
|
|
||||||
c: &Gles2,
|
|
||||||
sk_shader: &mut Shader,
|
|
||||||
vert_str: &str,
|
|
||||||
frag_str: &str,
|
|
||||||
) -> Result<(), GlesError> {
|
|
||||||
let gl_vert = compile_shader(c, VERTEX_SHADER, vert_str)?;
|
|
||||||
let gl_frag = compile_shader(c, FRAGMENT_SHADER, frag_str)?;
|
|
||||||
let gl_prog = link_program(c, gl_vert, gl_frag)?;
|
|
||||||
|
|
||||||
let shader: *mut FfiShader = transmute(sk_shader.0.as_mut());
|
|
||||||
if let Some(shader) = shader.as_mut() {
|
|
||||||
shader.shader.vertex = gl_vert;
|
|
||||||
shader.shader.pixel = gl_frag;
|
|
||||||
shader.shader.program = gl_prog;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
nodes::{
|
nodes::{
|
||||||
drawable::Drawable,
|
drawable::Drawable,
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
spatial::{find_spatial_parent, parse_transform, Spatial},
|
||||||
Message, Node,
|
Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
||||||
@@ -13,6 +13,7 @@ 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 prisma::{Flatten, Rgba};
|
||||||
|
use send_wrapper::SendWrapper;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stardust_xr::{schemas::flex::deserialize, values::Transform};
|
use stardust_xr::{schemas::flex::deserialize, values::Transform};
|
||||||
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
|
||||||
@@ -34,7 +35,7 @@ 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<SendWrapper<TextStyle>>,
|
||||||
|
|
||||||
data: Mutex<TextData>,
|
data: Mutex<TextData>,
|
||||||
}
|
}
|
||||||
@@ -90,16 +91,18 @@ impl Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, sk: &impl StereoKitDraw) {
|
fn draw(&self, sk: &impl StereoKitDraw) {
|
||||||
let style = self
|
let style = self.style.get_or_try_init(
|
||||||
.style
|
|| -> Result<SendWrapper<TextStyle>, color_eyre::eyre::Error> {
|
||||||
.get_or_try_init(|| -> Result<TextStyle, 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(SendWrapper::new(unsafe {
|
||||||
});
|
sk.text_make_style(font, 1.0, WHITE)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if let Ok(style) = style {
|
if let Ok(style) = style {
|
||||||
let data = self.data.lock();
|
let data = self.data.lock();
|
||||||
@@ -115,7 +118,7 @@ impl Text {
|
|||||||
transform,
|
transform,
|
||||||
bounds / data.character_height,
|
bounds / data.character_height,
|
||||||
data.fit,
|
data.fit,
|
||||||
*style,
|
**style,
|
||||||
data.bounds_align,
|
data.bounds_align,
|
||||||
data.text_align,
|
data.text_align,
|
||||||
vec3(0.0, 0.0, 0.0),
|
vec3(0.0, 0.0, 0.0),
|
||||||
@@ -130,7 +133,7 @@ impl Text {
|
|||||||
sk.text_add_at(
|
sk.text_add_at(
|
||||||
&data.text,
|
&data.text,
|
||||||
transform,
|
transform,
|
||||||
*style,
|
**style,
|
||||||
data.bounds_align,
|
data.bounds_align,
|
||||||
data.text_align,
|
data.text_align,
|
||||||
vec3(0.0, 0.0, 0.0),
|
vec3(0.0, 0.0, 0.0),
|
||||||
@@ -148,22 +151,18 @@ impl Text {
|
|||||||
pub fn set_character_height_flex(
|
pub fn set_character_height_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(Drawable::Text(text)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let Some(Drawable::Text(text)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||||
|
|
||||||
text.data.lock().character_height = deserialize(message.as_ref())?;
|
text.data.lock().character_height = deserialize(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text_flex(
|
pub fn set_text_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(Drawable::Text(text)) = node.drawable.get() else {bail!("Not a drawable??")};
|
let Some(Drawable::Text(text)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||||
|
|
||||||
text.data.lock().text = deserialize(message.as_ref())?;
|
text.data.lock().text = deserialize(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +183,7 @@ pub fn draw_all(sk: &impl StereoKitDraw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateTextInfo<'a> {
|
struct CreateTextInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -199,7 +198,7 @@ pub fn create_flex(_node: &Node, calling_client: Arc<Client>, message: Message)
|
|||||||
bounds_align: TextAlign,
|
bounds_align: TextAlign,
|
||||||
color: [f32; 4],
|
color: [f32; 4],
|
||||||
}
|
}
|
||||||
let info: CreateTextInfo = deserialize(message.as_ref())?;
|
let info: CreateTextInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/drawable/text", info.name, true);
|
let node = Node::create(&calling_client, "/drawable/text", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = parse_transform(info.transform, true, true, true);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{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::spatial::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use crate::nodes::Message;
|
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use glam::{vec3, vec3a, Vec3, Vec3A};
|
use glam::{vec3, vec3a, Vec3, Vec3A};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
@@ -17,7 +16,7 @@ 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>) -> Result<()> {
|
||||||
ensure!(
|
ensure!(
|
||||||
node.spatial.get().is_some(),
|
node.spatial.get().is_some(),
|
||||||
"Internal: Node does not have a spatial attached!"
|
"Internal: Node does not have a spatial attached!"
|
||||||
@@ -32,22 +31,17 @@ impl BoxField {
|
|||||||
};
|
};
|
||||||
box_field.add_field_methods(node);
|
box_field.add_field_methods(node);
|
||||||
node.add_local_signal("set_size", BoxField::set_size_flex);
|
node.add_local_signal("set_size", BoxField::set_size_flex);
|
||||||
let field = Arc::new(Field::Box(box_field));
|
let _ = node.field.set(Arc::new(Field::Box(box_field)));
|
||||||
let _ = node.field.set(field.clone());
|
Ok(())
|
||||||
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(
|
pub fn set_size_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Box(box_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
let Field::Box(box_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
||||||
box_field.set_size(deserialize(message.as_ref())?);
|
box_field.set_size(deserialize(data)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -69,11 +63,7 @@ impl FieldTrait for BoxField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_box_field_flex(
|
pub fn create_box_field_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateFieldInfo<'a> {
|
struct CreateFieldInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -81,7 +71,7 @@ pub fn create_box_field_flex(
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
size: Vector3<f32>,
|
size: Vector3<f32>,
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
let info: CreateFieldInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
let node = Node::create(&calling_client, "/field", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{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::spatial::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use crate::nodes::Message;
|
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use glam::{swizzles::*, vec2, Vec3A};
|
use glam::{swizzles::*, vec2, Vec3A};
|
||||||
use portable_atomic::AtomicF32;
|
use portable_atomic::AtomicF32;
|
||||||
@@ -44,13 +43,9 @@ impl CylinderField {
|
|||||||
self.radius.store(radius.abs(), Ordering::Relaxed);
|
self.radius.store(radius.abs(), Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size_flex(
|
pub fn set_size_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Cylinder(cylinder_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
let Field::Cylinder(cylinder_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
||||||
let (length, radius) = deserialize(message.as_ref())?;
|
let (length, radius) = deserialize(data)?;
|
||||||
cylinder_field.set_size(length, radius);
|
cylinder_field.set_size(length, radius);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -72,7 +67,7 @@ impl FieldTrait for CylinderField {
|
|||||||
pub fn create_cylinder_field_flex(
|
pub fn create_cylinder_field_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateFieldInfo<'a> {
|
struct CreateFieldInfo<'a> {
|
||||||
@@ -82,7 +77,7 @@ pub fn create_cylinder_field_flex(
|
|||||||
length: f32,
|
length: f32,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
let info: CreateFieldInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
let node = Node::create(&calling_client, "/field", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub mod r#box;
|
mod r#box;
|
||||||
mod cylinder;
|
mod cylinder;
|
||||||
mod sphere;
|
mod sphere;
|
||||||
mod torus;
|
pub mod torus;
|
||||||
|
|
||||||
use self::cylinder::{create_cylinder_field_flex, CylinderField};
|
use self::cylinder::{create_cylinder_field_flex, CylinderField};
|
||||||
use self::r#box::{create_box_field_flex, BoxField};
|
use self::r#box::{create_box_field_flex, BoxField};
|
||||||
@@ -10,9 +10,8 @@ use self::torus::{create_torus_field_flex, TorusField};
|
|||||||
|
|
||||||
use super::alias::AliasInfo;
|
use super::alias::AliasInfo;
|
||||||
use super::spatial::Spatial;
|
use super::spatial::Spatial;
|
||||||
use super::{Message, Node};
|
use super::Node;
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
|
||||||
use crate::nodes::spatial::find_reference_space;
|
use crate::nodes::spatial::find_reference_space;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec2, vec3a, Vec3, Vec3A};
|
use glam::{vec2, vec3a, Vec3, Vec3A};
|
||||||
@@ -134,98 +133,76 @@ 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(
|
fn field_distance_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<Vec<u8>> {
|
||||||
node: &Node,
|
#[derive(Deserialize)]
|
||||||
calling_client: Arc<Client>,
|
struct FieldInfoArgs<'a> {
|
||||||
message: Message,
|
reference_space_path: &'a str,
|
||||||
response: MethodResponseSender,
|
point: Vector3<f32>,
|
||||||
) {
|
}
|
||||||
response.wrap_sync(move || {
|
let args: FieldInfoArgs = deserialize(data)?;
|
||||||
#[derive(Deserialize)]
|
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
||||||
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
|
let distance = node
|
||||||
.field
|
.field
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.distance(reference_space.as_ref(), args.point.into());
|
.distance(reference_space.as_ref(), args.point.into());
|
||||||
Ok(serialize(distance)?.into())
|
Ok(serialize(distance)?)
|
||||||
});
|
|
||||||
}
|
}
|
||||||
fn field_normal_flex(
|
fn field_normal_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<Vec<u8>> {
|
||||||
node: &Node,
|
#[derive(Deserialize)]
|
||||||
calling_client: Arc<Client>,
|
struct FieldInfoArgs<'a> {
|
||||||
message: Message,
|
reference_space_path: &'a str,
|
||||||
response: MethodResponseSender,
|
point: Vector3<f32>,
|
||||||
) {
|
radius: Option<f32>,
|
||||||
response.wrap_sync(move || {
|
}
|
||||||
#[derive(Deserialize)]
|
let args: FieldInfoArgs = deserialize(data)?;
|
||||||
struct FieldInfoArgs<'a> {
|
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
||||||
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(
|
let normal = node.field.get().as_ref().unwrap().normal(
|
||||||
reference_space.as_ref(),
|
reference_space.as_ref(),
|
||||||
args.point.into(),
|
args.point.into(),
|
||||||
0.001,
|
args.radius.unwrap_or(0.001),
|
||||||
);
|
);
|
||||||
Ok(serialize(mint::Vector3::from(normal))?.into())
|
Ok(serialize(mint::Vector3::from(normal))?)
|
||||||
});
|
|
||||||
}
|
}
|
||||||
fn field_closest_point_flex(
|
fn field_closest_point_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>> {
|
||||||
) {
|
#[derive(Deserialize)]
|
||||||
response.wrap_sync(move || {
|
struct FieldInfoArgs<'a> {
|
||||||
#[derive(Deserialize)]
|
reference_space_path: &'a str,
|
||||||
struct FieldInfoArgs<'a> {
|
point: Vector3<f32>,
|
||||||
reference_space_path: &'a str,
|
radius: Option<f32>,
|
||||||
point: Vector3<f32>,
|
}
|
||||||
}
|
let args: FieldInfoArgs = deserialize(data)?;
|
||||||
let args: FieldInfoArgs = deserialize(message.as_ref())?;
|
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
||||||
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
|
||||||
|
|
||||||
let closest_point = node.field.get().as_ref().unwrap().closest_point(
|
let closest_point = node.field.get().as_ref().unwrap().closest_point(
|
||||||
reference_space.as_ref(),
|
reference_space.as_ref(),
|
||||||
args.point.into(),
|
args.point.into(),
|
||||||
0.001,
|
args.radius.unwrap_or(0.001),
|
||||||
);
|
);
|
||||||
Ok(serialize(mint::Vector3::from(closest_point))?.into())
|
Ok(serialize(mint::Vector3::from(closest_point))?)
|
||||||
});
|
|
||||||
}
|
}
|
||||||
fn field_ray_march_flex(
|
fn field_ray_march_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<Vec<u8>> {
|
||||||
node: &Node,
|
#[derive(Deserialize)]
|
||||||
calling_client: Arc<Client>,
|
struct FieldInfoArgs<'a> {
|
||||||
message: Message,
|
reference_space_path: &'a str,
|
||||||
response: MethodResponseSender,
|
ray_origin: Vector3<f32>,
|
||||||
) {
|
ray_direction: Vector3<f32>,
|
||||||
response.wrap_sync(move || {
|
}
|
||||||
#[derive(Deserialize)]
|
let args: FieldInfoArgs = deserialize(data)?;
|
||||||
struct FieldInfoArgs<'a> {
|
let reference_space = find_reference_space(&calling_client, args.reference_space_path)?;
|
||||||
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 {
|
let ray_march_result = node.field.get().unwrap().ray_march(Ray {
|
||||||
origin: args.ray_origin.into(),
|
origin: args.ray_origin.into(),
|
||||||
direction: args.ray_direction.into(),
|
direction: args.ray_direction.into(),
|
||||||
space: reference_space,
|
space: reference_space,
|
||||||
});
|
|
||||||
Ok(serialize(ray_march_result)?.into())
|
|
||||||
});
|
});
|
||||||
|
Ok(serialize(ray_march_result)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Field {
|
pub enum Field {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{Field, FieldTrait, Node};
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::nodes::spatial::{find_spatial_parent, Spatial};
|
use crate::nodes::spatial::{find_spatial_parent, Spatial};
|
||||||
use crate::nodes::Message;
|
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use glam::{Mat4, Vec3A};
|
use glam::{Mat4, Vec3A};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
@@ -40,13 +39,9 @@ impl SphereField {
|
|||||||
self.radius.store(radius, Ordering::Relaxed);
|
self.radius.store(radius, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_radius_flex(
|
pub fn set_radius_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Sphere(sphere_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
let Field::Sphere(sphere_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
||||||
sphere_field.set_radius(deserialize(message.as_ref())?);
|
sphere_field.set_radius(deserialize(data)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +64,7 @@ impl FieldTrait for SphereField {
|
|||||||
pub fn create_sphere_field_flex(
|
pub fn create_sphere_field_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateFieldInfo<'a> {
|
struct CreateFieldInfo<'a> {
|
||||||
@@ -78,7 +73,7 @@ pub fn create_sphere_field_flex(
|
|||||||
origin: Option<Vector3<f32>>,
|
origin: Option<Vector3<f32>>,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
let info: CreateFieldInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
let node = Node::create(&calling_client, "/field", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = Mat4::from_translation(
|
let transform = Mat4::from_translation(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::{Field, FieldTrait, Node};
|
use super::{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::spatial::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use crate::nodes::Message;
|
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use glam::{swizzles::*, vec2, Vec3A};
|
use glam::{swizzles::*, vec2, Vec3A};
|
||||||
use portable_atomic::AtomicF32;
|
use portable_atomic::AtomicF32;
|
||||||
@@ -44,13 +43,9 @@ impl TorusField {
|
|||||||
self.radius_b.store(radius_b.abs(), Ordering::Relaxed);
|
self.radius_b.store(radius_b.abs(), Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size_flex(
|
pub fn set_size_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Field::Torus(torus_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
let Field::Torus(torus_field) = node.field.get().unwrap().as_ref() else { return Ok(()) };
|
||||||
let (radius_a, radius_b) = deserialize(message.as_ref())?;
|
let (radius_a, radius_b) = deserialize(data)?;
|
||||||
torus_field.set_size(radius_a, radius_b);
|
torus_field.set_size(radius_a, radius_b);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -72,7 +67,7 @@ impl FieldTrait for TorusField {
|
|||||||
pub fn create_torus_field_flex(
|
pub fn create_torus_field_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateFieldInfo<'a> {
|
struct CreateFieldInfo<'a> {
|
||||||
@@ -82,7 +77,7 @@ pub fn create_torus_field_flex(
|
|||||||
radius_a: f32,
|
radius_a: f32,
|
||||||
radius_b: f32,
|
radius_b: f32,
|
||||||
}
|
}
|
||||||
let info: CreateFieldInfo = deserialize(message.as_ref())?;
|
let info: CreateFieldInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/field", info.name, true);
|
let node = Node::create(&calling_client, "/field", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use color_eyre::eyre::Result;
|
|||||||
use glam::{vec3, Mat4};
|
use glam::{vec3, Mat4};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::StereoKitMultiThread;
|
use stereokit::StereoKitMultiThread;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref HMD: Arc<Node> = create();
|
static ref HMD: Arc<Node> = create();
|
||||||
@@ -19,6 +20,7 @@ fn create() -> Arc<Node> {
|
|||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", name = "Update HMD Pose", skip(sk))]
|
||||||
pub fn frame(sk: &impl StereoKitMultiThread) {
|
pub fn frame(sk: &impl StereoKitMultiThread) {
|
||||||
let spatial = HMD
|
let spatial = HMD
|
||||||
.spatial
|
.spatial
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ impl InputSpecialization for Hand {
|
|||||||
}
|
}
|
||||||
fn serialize(
|
fn serialize(
|
||||||
&self,
|
&self,
|
||||||
distance_link: &DistanceLink,
|
_distance_link: &DistanceLink,
|
||||||
local_to_handler_matrix: Mat4,
|
local_to_handler_matrix: Mat4,
|
||||||
) -> InputDataType {
|
) -> InputDataType {
|
||||||
let mut hand = self.base;
|
let mut hand = self.base;
|
||||||
@@ -68,10 +68,6 @@ impl InputSpecialization for Hand {
|
|||||||
let (_, rotation, position) = joint_matrix.to_scale_rotation_translation();
|
let (_, rotation, position) = joint_matrix.to_scale_rotation_translation();
|
||||||
joint.position = position.into();
|
joint.position = position.into();
|
||||||
joint.rotation = rotation.into();
|
joint.rotation = rotation.into();
|
||||||
joint.distance = distance_link
|
|
||||||
.handler
|
|
||||||
.field
|
|
||||||
.distance(&distance_link.handler.spatial, position.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDataType::Hand(Box::new(hand))
|
InputDataType::Hand(Box::new(hand))
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ 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::{find_spatial_parent, parse_transform, Spatial},
|
||||||
Message, Node,
|
Node,
|
||||||
};
|
};
|
||||||
use crate::core::registry::Registry;
|
|
||||||
use crate::core::{client::Client, node_collections::LifeLinkedNodeMap};
|
use crate::core::{client::Client, node_collections::LifeLinkedNodeMap};
|
||||||
|
use crate::core::{node_collections::LifeLinkedNodeList, registry::Registry};
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
@@ -99,7 +99,6 @@ impl InputMethod {
|
|||||||
};
|
};
|
||||||
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
|
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
|
||||||
method.handle_new_handler(&handler);
|
method.handle_new_handler(&handler);
|
||||||
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());
|
let _ = node.input_method.set(method.clone());
|
||||||
@@ -110,21 +109,21 @@ impl InputMethod {
|
|||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capture_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn capture_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> 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(data)?)?;
|
||||||
|
|
||||||
method.captures.add_raw(&handler);
|
method.captures.add_raw(&handler);
|
||||||
node.send_remote_signal("capture", message)
|
node.send_remote_signal("capture", data)
|
||||||
}
|
}
|
||||||
fn set_datamap_flex(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn set_datamap_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
let method = InputMethod::get(node)?;
|
let method = InputMethod::get(node)?;
|
||||||
method.datamap.lock().replace(Datamap::new(message.data)?);
|
method.datamap.lock().replace(Datamap::new(data.to_vec())?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn set_handlers_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn set_handlers_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
let method = InputMethod::get(node)?;
|
let method = InputMethod::get(node)?;
|
||||||
let handler_paths: Vec<&str> = deserialize(message.as_ref())?;
|
let handler_paths: Vec<&str> = deserialize(data)?;
|
||||||
let handlers: Vec<Weak<InputHandler>> = handler_paths
|
let handlers: Vec<Weak<InputHandler>> = handler_paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|p| InputHandler::find(&calling_client, p).ok())
|
.filter_map(|p| InputHandler::find(&calling_client, p).ok())
|
||||||
@@ -138,26 +137,6 @@ impl InputMethod {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_alias(&self, handler: &InputHandler) {
|
|
||||||
let Some(method_node) = self.node.upgrade() 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(
|
|
||||||
&client,
|
|
||||||
handler_node.get_path(),
|
|
||||||
&self.uid,
|
|
||||||
&method_node,
|
|
||||||
AliasInfo {
|
|
||||||
server_signals: vec!["capture"],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
) else {return};
|
|
||||||
method_alias.enabled.store(false, Ordering::Relaxed);
|
|
||||||
handler
|
|
||||||
.method_aliases
|
|
||||||
.add(self as *const InputMethod as usize, &method_alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_distance(&self, to: &Field) -> f32 {
|
fn compare_distance(&self, to: &Field) -> f32 {
|
||||||
self.specialization
|
self.specialization
|
||||||
.lock()
|
.lock()
|
||||||
@@ -168,9 +147,9 @@ 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 { return };
|
||||||
let Some(method_client) = method_node.get_client() else {return};
|
let Some(method_client) = method_node.get_client() else { return };
|
||||||
let Some(handler_node) = handler.node.upgrade() 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,
|
||||||
@@ -199,7 +178,7 @@ impl InputMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
@@ -207,7 +186,7 @@ impl InputMethod {
|
|||||||
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 {return};
|
||||||
let Ok(data) = serialize(&uid) else {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 Drop for InputMethod {
|
impl Drop for InputMethod {
|
||||||
@@ -222,12 +201,25 @@ pub struct DistanceLink {
|
|||||||
handler: Arc<InputHandler>,
|
handler: Arc<InputHandler>,
|
||||||
}
|
}
|
||||||
impl DistanceLink {
|
impl DistanceLink {
|
||||||
fn from(method: Arc<InputMethod>, handler: Arc<InputHandler>) -> Self {
|
fn from(method: Arc<InputMethod>, handler: Arc<InputHandler>) -> Option<Self> {
|
||||||
DistanceLink {
|
let handler_node = handler.node.upgrade()?;
|
||||||
|
let method_alias = Alias::create(
|
||||||
|
&handler_node.get_client()?,
|
||||||
|
handler_node.get_path(),
|
||||||
|
&method.uid,
|
||||||
|
&method.node.upgrade()?,
|
||||||
|
AliasInfo {
|
||||||
|
server_signals: vec!["capture"],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
handler.method_aliases.add(Arc::downgrade(&method_alias));
|
||||||
|
Some(DistanceLink {
|
||||||
distance: method.compare_distance(&handler.field),
|
distance: method.compare_distance(&handler.field),
|
||||||
method,
|
method,
|
||||||
handler,
|
handler,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_input(&self, order: u32, datamap: Datamap) {
|
fn send_input(&self, order: u32, datamap: Datamap) {
|
||||||
@@ -257,7 +249,7 @@ pub struct InputHandler {
|
|||||||
node: Weak<Node>,
|
node: Weak<Node>,
|
||||||
spatial: Arc<Spatial>,
|
spatial: Arc<Spatial>,
|
||||||
field: Arc<Field>,
|
field: Arc<Field>,
|
||||||
method_aliases: LifeLinkedNodeMap<usize>,
|
method_aliases: LifeLinkedNodeList,
|
||||||
}
|
}
|
||||||
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<()> {
|
||||||
@@ -272,10 +264,9 @@ impl InputHandler {
|
|||||||
node: Arc::downgrade(node),
|
node: Arc::downgrade(node),
|
||||||
spatial: node.spatial.get().unwrap().clone(),
|
spatial: node.spatial.get().unwrap().clone(),
|
||||||
field: field.clone(),
|
field: field.clone(),
|
||||||
method_aliases: LifeLinkedNodeMap::default(),
|
method_aliases: LifeLinkedNodeList::default(),
|
||||||
};
|
};
|
||||||
for method in INPUT_METHOD_REGISTRY.get_valid_contents() {
|
for method in INPUT_METHOD_REGISTRY.get_valid_contents() {
|
||||||
method.make_alias(&handler);
|
|
||||||
method.handle_new_handler(&handler);
|
method.handle_new_handler(&handler);
|
||||||
}
|
}
|
||||||
let handler = INPUT_HANDLER_REGISTRY.add(handler);
|
let handler = INPUT_HANDLER_REGISTRY.add(handler);
|
||||||
@@ -293,7 +284,7 @@ impl InputHandler {
|
|||||||
#[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(&self, order: u32, distance_link: &DistanceLink, datamap: Datamap) {
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
let Some(node) = self.node.upgrade() else {return};
|
||||||
let _ = node.send_remote_signal("input", distance_link.serialize(order, datamap));
|
let _ = node.send_remote_signal("input", &distance_link.serialize(order, datamap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl PartialEq for InputHandler {
|
impl PartialEq for InputHandler {
|
||||||
@@ -321,7 +312,7 @@ pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|||||||
pub fn create_input_handler_flex(
|
pub fn create_input_handler_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateInputHandlerInfo<'a> {
|
struct CreateInputHandlerInfo<'a> {
|
||||||
@@ -330,7 +321,7 @@ pub fn create_input_handler_flex(
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
field_path: &'a str,
|
field_path: &'a str,
|
||||||
}
|
}
|
||||||
let info: CreateInputHandlerInfo = deserialize(message.as_ref())?;
|
let info: CreateInputHandlerInfo = deserialize(data)?;
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
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)?;
|
||||||
@@ -352,12 +343,10 @@ pub fn process_input() {
|
|||||||
.filter(|method| method.datamap.lock().is_some())
|
.filter(|method| method.datamap.lock().is_some())
|
||||||
});
|
});
|
||||||
let handlers = INPUT_HANDLER_REGISTRY.get_valid_contents();
|
let handlers = INPUT_HANDLER_REGISTRY.get_valid_contents();
|
||||||
const LIMIT: usize = 50;
|
for handler in &handlers {
|
||||||
|
handler.method_aliases.clear();
|
||||||
|
}
|
||||||
for method in methods {
|
for method in methods {
|
||||||
for alias in method.node.upgrade().unwrap().aliases.get_valid_contents() {
|
|
||||||
alias.enabled.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_span!("Process input method").in_scope(|| {
|
debug_span!("Process input method").in_scope(|| {
|
||||||
// Get all valid input handlers and convert them to DistanceLink objects
|
// Get all valid input handlers and convert them to DistanceLink objects
|
||||||
let distance_links: Vec<DistanceLink> = debug_span!("Generate distance links")
|
let distance_links: Vec<DistanceLink> = debug_span!("Generate distance links")
|
||||||
@@ -368,16 +357,14 @@ pub fn process_input() {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|h| h.upgrade())
|
.filter_map(|h| h.upgrade())
|
||||||
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
|
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
|
||||||
.map(|handler| DistanceLink::from(method.clone(), handler))
|
.filter_map(|handler| DistanceLink::from(method.clone(), handler))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
let mut distance_links: Vec<_> = handlers
|
let mut distance_links: Vec<_> = handlers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
|
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
|
||||||
.map(|handler| {
|
.filter_map(|handler| {
|
||||||
debug_span!("Create distance link").in_scope(|| {
|
DistanceLink::from(method.clone(), handler.clone())
|
||||||
DistanceLink::from(method.clone(), handler.clone())
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -388,7 +375,6 @@ pub fn process_input() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
distance_links.truncate(LIMIT);
|
|
||||||
distance_links
|
distance_links
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -396,18 +382,6 @@ 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
|
|
||||||
.handler
|
|
||||||
.method_aliases
|
|
||||||
.get(&(Arc::as_ptr(&distance_link.method) as usize))
|
|
||||||
.and_then(|a| a.alias.get().cloned())
|
|
||||||
{
|
|
||||||
method_alias.enabled.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
distance_link.send_input(i as u32, method.datamap.lock().clone().unwrap());
|
distance_link.send_input(i as u32, 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,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use crate::nodes::{Message, Node};
|
use crate::nodes::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::{Datamap, InputDataType, Pointer as FlatPointer};
|
||||||
@@ -34,13 +34,9 @@ impl Pointer {
|
|||||||
impl InputSpecialization for Pointer {
|
impl InputSpecialization for Pointer {
|
||||||
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
||||||
let ray_info = self.ray_march(space, field);
|
let ray_info = self.ray_march(space, field);
|
||||||
if ray_info.min_distance > 0.0 {
|
ray_info
|
||||||
ray_info.deepest_point_distance + 1000.0
|
.deepest_point_distance
|
||||||
} else {
|
.hypot(ray_info.min_distance.recip())
|
||||||
ray_info
|
|
||||||
.deepest_point_distance
|
|
||||||
.hypot(0.001 / ray_info.min_distance)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
||||||
let ray_info = self.ray_march(space, field);
|
let ray_info = self.ray_march(space, field);
|
||||||
@@ -67,7 +63,7 @@ impl InputSpecialization for Pointer {
|
|||||||
pub fn create_pointer_flex(
|
pub fn create_pointer_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> color_eyre::eyre::Result<()> {
|
) -> color_eyre::eyre::Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreatePointerInfo<'a> {
|
struct CreatePointerInfo<'a> {
|
||||||
@@ -76,7 +72,7 @@ pub fn create_pointer_flex(
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
datamap: Option<Vec<u8>>,
|
datamap: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
let info: CreatePointerInfo = deserialize(message.as_ref())?;
|
let info: CreatePointerInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/input/method/pointer", info.name, true);
|
let node = Node::create(&calling_client, "/input/method/pointer", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use crate::nodes::{Message, Node};
|
use crate::nodes::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;
|
||||||
@@ -17,9 +17,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: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
if let InputType::Tip(tip) = &mut *node.input_method.get().unwrap().specialization.lock() {
|
if let InputType::Tip(tip) = &mut *node.input_method.get().unwrap().specialization.lock() {
|
||||||
tip.radius = deserialize(message.as_ref())?;
|
tip.radius = deserialize(data)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ impl InputSpecialization for Tip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_tip_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_tip_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateTipInfo<'a> {
|
struct CreateTipInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -54,7 +54,7 @@ pub fn create_tip_flex(_node: &Node, calling_client: Arc<Client>, message: Messa
|
|||||||
radius: f32,
|
radius: f32,
|
||||||
datamap: Option<Vec<u8>>,
|
datamap: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
let info: CreateTipInfo = deserialize(message.as_ref())?;
|
let info: CreateTipInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/input/method/tip", info.name, true);
|
let node = Node::create(&calling_client, "/input/method/tip", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|||||||
@@ -1,357 +0,0 @@
|
|||||||
use super::{Item, ItemType};
|
|
||||||
use crate::{
|
|
||||||
core::{
|
|
||||||
buffers::BufferManager,
|
|
||||||
client::{Client, INTERNAL_CLIENT},
|
|
||||||
registry::Registry,
|
|
||||||
scenegraph::MethodResponseSender,
|
|
||||||
},
|
|
||||||
nodes::{
|
|
||||||
drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES, Drawable},
|
|
||||||
items::TypeInfo,
|
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
|
||||||
Message, Node,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use color_eyre::eyre::{bail, Result};
|
|
||||||
use glam::Mat4;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use mint::{RowMatrix4, Vector2};
|
|
||||||
use nanoid::nanoid;
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use smithay::backend::{
|
|
||||||
allocator::{
|
|
||||||
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::{
|
|
||||||
Color128, Material, Rect, RenderLayer, StereoKitDraw, Tex, TextureFormat, TextureType,
|
|
||||||
Transparency,
|
|
||||||
};
|
|
||||||
use tokio::sync::mpsc::{self, error::SendError};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub(super) static ref ITEM_TYPE_INFO_CAMERA: TypeInfo = TypeInfo {
|
|
||||||
type_name: "camera",
|
|
||||||
aliased_local_signals: vec!["apply_preview_material", "set_proj_matrix"],
|
|
||||||
aliased_local_methods: vec!["render"],
|
|
||||||
aliased_remote_signals: vec![],
|
|
||||||
ui: Default::default(),
|
|
||||||
items: Registry::new(),
|
|
||||||
acceptors: Registry::new(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FrameInfo {
|
|
||||||
proj_matrix: Mat4,
|
|
||||||
preview_size: Vector2<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CameraItem {
|
|
||||||
space: Arc<Spatial>,
|
|
||||||
frame_info: Mutex<FrameInfo>,
|
|
||||||
sk_tex: OnceCell<Tex>,
|
|
||||||
sk_mat: OnceCell<Arc<Material>>,
|
|
||||||
render_requests_tx: mpsc::UnboundedSender<(Dmabuf, MethodResponseSender)>,
|
|
||||||
render_requests_rx: Mutex<mpsc::UnboundedReceiver<(Dmabuf, MethodResponseSender)>>,
|
|
||||||
rendered_notifiers: Mutex<Vec<MethodResponseSender>>,
|
|
||||||
applied_preview_to: Registry<ModelPart>,
|
|
||||||
apply_preview_to: Registry<ModelPart>,
|
|
||||||
}
|
|
||||||
impl CameraItem {
|
|
||||||
pub fn add_to(node: &Arc<Node>, proj_matrix: Mat4, preview_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(
|
|
||||||
node,
|
|
||||||
nanoid!(),
|
|
||||||
&ITEM_TYPE_INFO_CAMERA,
|
|
||||||
ItemType::Camera(camera_specialization),
|
|
||||||
);
|
|
||||||
node.add_local_method("render", CameraItem::render_flex);
|
|
||||||
node.add_local_signal(
|
|
||||||
"apply_preview_material",
|
|
||||||
CameraItem::apply_preview_material_flex,
|
|
||||||
);
|
|
||||||
node.add_local_signal("set_proj_matrix", CameraItem::set_proj_matrix_flex);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
let Some(item) = node.item.get() else {
|
|
||||||
let _ = response.send(Err(ScenegraphError::MethodError {
|
|
||||||
error: "Item not found".to_string(),
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
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(
|
|
||||||
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 model_part_path = deserialize(&message.data)?;
|
|
||||||
let model_part_node = calling_client.get_node("Model part", model_part_path)?;
|
|
||||||
let Drawable::ModelPart(model_part) =
|
|
||||||
model_part_node.get_aspect("Model part", "model part", |n| &n.drawable)?
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serialize_start_data(&self, id: &str) -> Result<Message> {
|
|
||||||
Ok(serialize(id)?.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, sk: &impl StereoKitDraw, buffer_manager: &mut BufferManager) {
|
|
||||||
let frame_info = self.frame_info.lock();
|
|
||||||
self.render_preview(sk, &*frame_info);
|
|
||||||
self.render_dmabuf(sk, buffer_manager, &*frame_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_preview(&self, sk: &impl StereoKitDraw, frame_info: &FrameInfo) {
|
|
||||||
if !self.apply_preview_to.is_empty() {
|
|
||||||
let sk_tex = self.sk_tex.get_or_init(|| {
|
|
||||||
sk.tex_gen_color(
|
|
||||||
Color128::default(),
|
|
||||||
frame_info.preview_size.x as i32,
|
|
||||||
frame_info.preview_size.y as i32,
|
|
||||||
TextureType::RENDER_TARGET,
|
|
||||||
TextureFormat::RGBA32Linear,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let sk_mat = self.sk_mat.get_or_init(|| {
|
|
||||||
let shader = sk.shader_create_mem(&UNLIT_SHADER_BYTES).unwrap();
|
|
||||||
let mat = sk.material_create(&shader);
|
|
||||||
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() {
|
|
||||||
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_tex,
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
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) {
|
|
||||||
for camera in ITEM_TYPE_INFO_CAMERA.items.get_valid_contents() {
|
|
||||||
let ItemType::Camera(camera) = &camera.specialization else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
camera.update(sk, buffer_manager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateCameraItemInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
parent_path: &'a str,
|
|
||||||
transform: Transform,
|
|
||||||
proj_matrix: RowMatrix4<f32>,
|
|
||||||
preview_size: Vector2<u32>,
|
|
||||||
}
|
|
||||||
let info: CreateCameraItemInfo = deserialize(message.as_ref())?;
|
|
||||||
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_CAMERA.type_name);
|
|
||||||
let space = find_spatial_parent(&calling_client, info.parent_path)?;
|
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
|
||||||
|
|
||||||
let node =
|
|
||||||
Node::create(&INTERNAL_CLIENT, &parent_name, info.name, false).add_to_scenegraph()?;
|
|
||||||
Spatial::add_to(&node, None, transform * space.global_transform(), false)?;
|
|
||||||
CameraItem::add_to(&node, info.proj_matrix.into(), info.preview_size);
|
|
||||||
// TODO: this means ANY client can render anywhere with no limits, this needs to be changed!!
|
|
||||||
node.item
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.make_alias_named(&calling_client, &parent_name, info.name)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
use super::{Item, ItemType};
|
use super::{Item, ItemSpecialization, ItemType};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
client::{Client, INTERNAL_CLIENT},
|
client::{Client, INTERNAL_CLIENT},
|
||||||
registry::Registry,
|
registry::Registry,
|
||||||
scenegraph::MethodResponseSender,
|
|
||||||
},
|
},
|
||||||
nodes::{
|
nodes::{
|
||||||
items::TypeInfo,
|
items::TypeInfo,
|
||||||
spatial::{find_spatial_parent, parse_transform, Spatial},
|
spatial::{find_spatial_parent, parse_transform, Spatial},
|
||||||
Message, Node,
|
Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
@@ -16,7 +15,7 @@ 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, flexbuffers, serialize},
|
||||||
values::Transform,
|
values::Transform,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -47,29 +46,23 @@ impl EnvironmentItem {
|
|||||||
node.add_local_method("get_path", EnvironmentItem::get_path_flex);
|
node.add_local_method("get_path", EnvironmentItem::get_path_flex);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_path_flex(
|
fn get_path_flex(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<Vec<u8>> {
|
||||||
node: &Node,
|
let ItemType::Environment(environment_item) = &node.item.get().unwrap().specialization else {
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
_message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let ItemType::Environment(environment_item) = &node.item.get().unwrap().specialization else {
|
|
||||||
return Err(eyre!("Wrong item type?"))
|
return Err(eyre!("Wrong item type?"))
|
||||||
};
|
};
|
||||||
Ok(serialize(environment_item.path.as_str())?.into())
|
Ok(flexbuffers::singleton(environment_item.path.as_str()))
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn serialize_start_data(&self, id: &str) -> Result<Message> {
|
impl ItemSpecialization for EnvironmentItem {
|
||||||
Ok(serialize((id, self.path.as_str()))?.into())
|
fn serialize_start_data(&self, id: &str) -> Vec<u8> {
|
||||||
|
serialize((id, self.path.as_str())).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn create_environment_item_flex(
|
pub(super) fn create_environment_item_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateEnvironmentItemInfo<'a> {
|
struct CreateEnvironmentItemInfo<'a> {
|
||||||
@@ -78,7 +71,7 @@ pub(super) fn create_environment_item_flex(
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
item_data: String,
|
item_data: String,
|
||||||
}
|
}
|
||||||
let info: CreateEnvironmentItemInfo = deserialize(message.as_ref())?;
|
let info: CreateEnvironmentItemInfo = deserialize(data)?;
|
||||||
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 = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, false);
|
let transform = parse_transform(info.transform, true, true, false);
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
pub mod camera;
|
|
||||||
mod environment;
|
mod environment;
|
||||||
pub mod panel;
|
|
||||||
|
|
||||||
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 super::fields::Field;
|
use super::fields::Field;
|
||||||
use super::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use super::spatial::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use super::{Alias, Message, Node};
|
use super::{Alias, 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;
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
use crate::wayland::panel_item::{PanelItem, ITEM_TYPE_INFO_PANEL};
|
||||||
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;
|
||||||
use parking_lot::Mutex;
|
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, flexbuffers, serialize};
|
||||||
use stardust_xr::values::Transform;
|
use stardust_xr::values::Transform;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -39,8 +38,8 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn capture(item: &Arc<Item>, acceptor: &Arc<ItemAcceptor>) {
|
pub fn capture(item: &Arc<Item>, acceptor: &Arc<ItemAcceptor>) {
|
||||||
if item.captured_acceptor.lock().strong_count() > 0 {
|
if let Some(acceptor) = item.captured_acceptor.lock().upgrade() {
|
||||||
release(item);
|
release(item, Some(&acceptor));
|
||||||
}
|
}
|
||||||
*item.captured_acceptor.lock() = Arc::downgrade(acceptor);
|
*item.captured_acceptor.lock() = Arc::downgrade(acceptor);
|
||||||
acceptor.handle_capture(item);
|
acceptor.handle_capture(item);
|
||||||
@@ -48,9 +47,9 @@ pub fn capture(item: &Arc<Item>, acceptor: &Arc<ItemAcceptor>) {
|
|||||||
ui.handle_capture_item(item, acceptor);
|
ui.handle_capture_item(item, acceptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn release(item: &Item) {
|
fn release(item: &Item, acceptor: Option<&ItemAcceptor>) {
|
||||||
let mut captured_acceptor = item.captured_acceptor.lock();
|
let mut captured_acceptor = item.captured_acceptor.lock();
|
||||||
if let Some(acceptor) = captured_acceptor.upgrade().as_ref() {
|
if let Some(acceptor) = captured_acceptor.upgrade().as_deref().or(acceptor) {
|
||||||
*captured_acceptor = Weak::default();
|
*captured_acceptor = Weak::default();
|
||||||
acceptor.handle_release(item);
|
acceptor.handle_release(item);
|
||||||
if let Some(ui) = item.type_info.ui.lock().upgrade() {
|
if let Some(ui) = item.type_info.ui.lock().upgrade() {
|
||||||
@@ -109,15 +108,15 @@ impl Item {
|
|||||||
}
|
}
|
||||||
let _ = node.item.set(item.clone());
|
let _ = node.item.set(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
|
||||||
// .state
|
.startup_settings
|
||||||
// .as_ref()
|
.as_ref()
|
||||||
// .and_then(|settings| settings.acceptors.get(type_info))
|
.and_then(|settings| settings.acceptors.get(type_info))
|
||||||
// .and_then(|acceptor| acceptor.upgrade())
|
.and_then(|acceptor| acceptor.upgrade())
|
||||||
// }) {
|
}) {
|
||||||
// capture(&item, &auto_acceptor);
|
capture(&item, &auto_acceptor);
|
||||||
// }
|
}
|
||||||
|
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
@@ -155,9 +154,9 @@ 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(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
|
||||||
let item = node.get_aspect("Item", "item", |n| &n.item)?;
|
let item = node.get_aspect("Item", "item", |n| &n.item)?;
|
||||||
release(item);
|
release(item, None);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -165,37 +164,33 @@ impl 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);
|
||||||
release(self);
|
release(self, None);
|
||||||
if let Some(ui) = self.type_info.ui.lock().upgrade() {
|
if let Some(ui) = self.type_info.ui.lock().upgrade() {
|
||||||
ui.handle_destroy_item(self);
|
ui.handle_destroy_item(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ItemType {
|
pub trait ItemSpecialization {
|
||||||
Camera(CameraItem),
|
fn serialize_start_data(&self, id: &str) -> Vec<u8>;
|
||||||
Environment(EnvironmentItem),
|
|
||||||
Panel(Arc<dyn PanelItemTrait>),
|
|
||||||
}
|
}
|
||||||
impl ItemType {
|
|
||||||
fn serialize_start_data(&self, id: &str) -> Result<Message> {
|
pub enum ItemType {
|
||||||
|
Environment(EnvironmentItem),
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
Panel(Arc<PanelItem>),
|
||||||
|
}
|
||||||
|
impl Deref for ItemType {
|
||||||
|
type Target = dyn ItemSpecialization;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
match self {
|
match self {
|
||||||
ItemType::Camera(c) => c.serialize_start_data(id),
|
ItemType::Environment(item) => item,
|
||||||
ItemType::Environment(e) => e.serialize_start_data(id),
|
#[cfg(feature = "wayland")]
|
||||||
ItemType::Panel(p) => p.serialize_start_data(id),
|
ItemType::Panel(item) => item.as_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// impl Deref for ItemType {
|
|
||||||
// type Target = dyn ItemSpecialization;
|
|
||||||
|
|
||||||
// fn deref(&self) -> &Self::Target {
|
|
||||||
// match self {
|
|
||||||
// ItemType::Environment(item) => item,
|
|
||||||
// ItemType::Panel(item) => item.as_ref(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub struct ItemUI {
|
pub struct ItemUI {
|
||||||
node: Weak<Node>,
|
node: Weak<Node>,
|
||||||
@@ -230,44 +225,49 @@ 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 _ = self
|
let _ = self
|
||||||
.node
|
.node
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.send_remote_signal(state, serialized_data);
|
.send_remote_signal(state, flexbuffers::singleton(name).as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
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 { return };
|
||||||
let Some(client) = node.get_client() else {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 _ = node.send_remote_signal(
|
||||||
let _ = node.send_remote_signal("create_item", serialized_data);
|
"create_item",
|
||||||
|
&item.specialization.serialize_start_data(&item.uid),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
fn handle_destroy_item(&self, item: &Item) {
|
fn handle_destroy_item(&self, item: &Item) {
|
||||||
self.item_aliases.remove(&item.uid);
|
self.item_aliases.remove(&item.uid);
|
||||||
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 _ = node.send_remote_signal(
|
||||||
let _ = node.send_remote_signal("capture_item", message);
|
"capture_item",
|
||||||
|
&serialize((item.uid.as_str(), acceptor.uid.as_str())).unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
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 _ = node.send_remote_signal(
|
||||||
let _ = node.send_remote_signal("release_item", message);
|
"release_item",
|
||||||
|
&serialize((item.uid.as_str(), acceptor.uid.as_str())).unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
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 { return };
|
||||||
let Some(client) = node.get_client() else {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,
|
||||||
@@ -276,8 +276,7 @@ impl ItemUI {
|
|||||||
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 _ = node.send_remote_signal("create_acceptor", &serialize(&acceptor.uid).unwrap());
|
||||||
let _ = node.send_remote_signal("create_acceptor", message);
|
|
||||||
}
|
}
|
||||||
fn handle_destroy_acceptor(&self, acceptor: &ItemAcceptor) {
|
fn handle_destroy_acceptor(&self, acceptor: &ItemAcceptor) {
|
||||||
self.send_state("destroy_acceptor", acceptor.uid.as_str());
|
self.send_state("destroy_acceptor", acceptor.uid.as_str());
|
||||||
@@ -316,13 +315,13 @@ impl ItemAcceptor {
|
|||||||
let _ = node.item_acceptor.set(acceptor);
|
let _ = node.item_acceptor.set(acceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capture_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn capture_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> 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.item_acceptor.get().unwrap();
|
||||||
let item_path: &str = deserialize(message.as_ref())?;
|
let item_path: &str = deserialize(data)?;
|
||||||
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", "item", |n| &n.item)?;
|
||||||
capture(item, acceptor);
|
capture(item, acceptor);
|
||||||
@@ -354,31 +353,31 @@ 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 { return };
|
||||||
let Some(client) = node.get_client() else {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 _ = node.send_remote_signal(
|
||||||
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
"capture",
|
||||||
let _ = node.send_remote_signal("capture", serialized_data);
|
&item.specialization.serialize_start_data(&item.uid),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
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 _ = node.send_remote_signal("release", &serialize(&item.uid).unwrap());
|
||||||
let _ = node.send_remote_signal("release", message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
for item in self.accepted_registry.get_valid_contents() {
|
for item in self.accepted_registry.get_valid_contents() {
|
||||||
release(&item);
|
release(&item, Some(self));
|
||||||
}
|
}
|
||||||
if let Some(ui) = self.type_info.ui.lock().upgrade() {
|
if let Some(ui) = self.type_info.ui.lock().upgrade() {
|
||||||
ui.handle_destroy_acceptor(self);
|
ui.handle_destroy_acceptor(self);
|
||||||
@@ -388,7 +387,6 @@ 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(client, "", "item", false);
|
||||||
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",
|
||||||
environment::create_environment_item_flex,
|
environment::create_environment_item_flex,
|
||||||
@@ -407,16 +405,12 @@ fn type_info(name: &str) -> Result<&'static TypeInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_item_ui_flex(
|
pub fn register_item_ui_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct RegisterItemUIInfo<'a> {
|
struct RegisterItemUIInfo<'a> {
|
||||||
item_type: &'a str,
|
item_type: &'a str,
|
||||||
}
|
}
|
||||||
let info: RegisterItemUIInfo = deserialize(message.as_ref())?;
|
let info: RegisterItemUIInfo = deserialize(data)?;
|
||||||
let type_info = type_info(info.item_type)?;
|
let type_info = type_info(info.item_type)?;
|
||||||
let ui =
|
let ui =
|
||||||
Node::create(&calling_client, "/item", type_info.type_name, true).add_to_scenegraph()?;
|
Node::create(&calling_client, "/item", type_info.type_name, true).add_to_scenegraph()?;
|
||||||
@@ -424,11 +418,7 @@ pub fn register_item_ui_flex(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_item_acceptor_flex(
|
fn create_item_acceptor_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateItemAcceptorInfo<'a> {
|
struct CreateItemAcceptorInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -437,7 +427,7 @@ fn create_item_acceptor_flex(
|
|||||||
field_path: &'a str,
|
field_path: &'a str,
|
||||||
item_type: &'a str,
|
item_type: &'a str,
|
||||||
}
|
}
|
||||||
let info: CreateItemAcceptorInfo = deserialize(message.as_ref())?;
|
let info: CreateItemAcceptorInfo = deserialize(data)?;
|
||||||
let space = find_spatial_parent(&calling_client, info.parent_path)?;
|
let space = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
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)?;
|
||||||
|
|||||||
@@ -1,542 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
core::{
|
|
||||||
client::{get_env, state, Client, INTERNAL_CLIENT},
|
|
||||||
registry::Registry,
|
|
||||||
},
|
|
||||||
nodes::{
|
|
||||||
drawable::{model::ModelPart, Drawable},
|
|
||||||
items::{Item, ItemType, TypeInfo},
|
|
||||||
spatial::Spatial,
|
|
||||||
Message, Node,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use color_eyre::eyre::{bail, eyre, Result};
|
|
||||||
use glam::Mat4;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use mint::Vector2;
|
|
||||||
use nanoid::nanoid;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{
|
|
||||||
de::{Deserializer, Error, SeqAccess, Visitor},
|
|
||||||
ser::Serializer,
|
|
||||||
Deserialize, Serialize,
|
|
||||||
};
|
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
|
||||||
use std::sync::{Arc, Weak};
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
|
||||||
type_name: "panel",
|
|
||||||
aliased_local_signals: vec![
|
|
||||||
"apply_surface_material",
|
|
||||||
"close_toplevel",
|
|
||||||
"auto_size_toplevel",
|
|
||||||
"set_toplevel_size",
|
|
||||||
"set_toplevel_focused_visuals",
|
|
||||||
"pointer_motion",
|
|
||||||
"pointer_button",
|
|
||||||
"pointer_scroll",
|
|
||||||
"keyboard_keymap",
|
|
||||||
"keyboard_key",
|
|
||||||
"touch_down",
|
|
||||||
"touch_move",
|
|
||||||
"touch_up",
|
|
||||||
"reset_touches",
|
|
||||||
],
|
|
||||||
aliased_local_methods: vec![],
|
|
||||||
aliased_remote_signals: vec![
|
|
||||||
"toplevel_parent_changed",
|
|
||||||
"toplevel_title_changed",
|
|
||||||
"toplevel_app_id_changed",
|
|
||||||
"toplevel_fullscreen_active",
|
|
||||||
"toplevel_move_request",
|
|
||||||
"toplevel_resize_request",
|
|
||||||
"toplevel_size_changed",
|
|
||||||
"set_cursor",
|
|
||||||
"new_child",
|
|
||||||
"reposition_child",
|
|
||||||
"drop_child",
|
|
||||||
],
|
|
||||||
ui: Default::default(),
|
|
||||||
items: Registry::new(),
|
|
||||||
acceptors: Registry::new(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An ID for a surface inside this panel item
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum SurfaceID {
|
|
||||||
Cursor,
|
|
||||||
Toplevel,
|
|
||||||
Child(String),
|
|
||||||
}
|
|
||||||
impl Default for SurfaceID {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Toplevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for SurfaceID {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
deserializer.deserialize_seq(SurfaceIDVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SurfaceIDVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for SurfaceIDVisitor {
|
|
||||||
type Value = SurfaceID;
|
|
||||||
|
|
||||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
f.write_str("idk")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
|
||||||
let Some(discrim) = seq.next_element()? else {
|
|
||||||
return Err(A::Error::missing_field("discrim"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// idk if you wanna check for extraneous elements
|
|
||||||
// I didn't bother
|
|
||||||
|
|
||||||
match discrim {
|
|
||||||
"Cursor" => Ok(SurfaceID::Cursor),
|
|
||||||
"Toplevel" => Ok(SurfaceID::Toplevel),
|
|
||||||
"Child" => {
|
|
||||||
let Some(text) = seq.next_element()? else {
|
|
||||||
return Err(A::Error::missing_field("child_text"));
|
|
||||||
};
|
|
||||||
Ok(SurfaceID::Child(text))
|
|
||||||
}
|
|
||||||
_ => Err(A::Error::unknown_variant(
|
|
||||||
discrim,
|
|
||||||
&["Cursor", "Toplevel", "Child"],
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl serde::Serialize for SurfaceID {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
match self {
|
|
||||||
Self::Cursor => ["Cursor"].serialize(serializer),
|
|
||||||
Self::Toplevel => ["Toplevel"].serialize(serializer),
|
|
||||||
Self::Child(text) => ["Child", text].serialize(serializer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The origin and size of the surface's "solid" part.
|
|
||||||
#[derive(Debug, Serialize, Clone, Copy)]
|
|
||||||
pub struct Geometry {
|
|
||||||
pub origin: Vector2<i32>,
|
|
||||||
pub size: Vector2<u32>,
|
|
||||||
}
|
|
||||||
/// The state of the panel item's toplevel.
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
pub struct ToplevelInfo {
|
|
||||||
/// The UID of the panel item of the parent of this toplevel, if it exists
|
|
||||||
pub parent: Option<String>,
|
|
||||||
/// Equivalent to the window title
|
|
||||||
pub title: Option<String>,
|
|
||||||
/// Application identifier, see <https://standards.freedesktop.org/desktop-entry-spec/>
|
|
||||||
pub app_id: Option<String>,
|
|
||||||
/// Current size in pixels
|
|
||||||
pub size: Vector2<u32>,
|
|
||||||
/// Recommended minimum size in pixels
|
|
||||||
pub min_size: Option<Vector2<u32>>,
|
|
||||||
/// Recommended maximum size in pixels
|
|
||||||
pub max_size: Option<Vector2<u32>>,
|
|
||||||
/// Surface geometry
|
|
||||||
pub logical_rectangle: Geometry,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data on positioning a child
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
pub struct ChildInfo {
|
|
||||||
pub parent: SurfaceID,
|
|
||||||
pub geometry: Geometry,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The init data for the panel item.
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
pub struct PanelItemInitData {
|
|
||||||
/// The cursor, if applicable.
|
|
||||||
pub cursor: Option<Geometry>,
|
|
||||||
/// Size of the toplevel surface in pixels.
|
|
||||||
pub toplevel: ToplevelInfo,
|
|
||||||
/// Vector of childs that already exist
|
|
||||||
pub children: FxHashMap<String, ChildInfo>,
|
|
||||||
/// The surface, if any, that has exclusive input to the pointer.
|
|
||||||
pub pointer_grab: Option<SurfaceID>,
|
|
||||||
/// The surface, if any, that has exclusive input to the keyboard.
|
|
||||||
pub keyboard_grab: Option<SurfaceID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Backend: Send + Sync + 'static {
|
|
||||||
fn start_data(&self) -> Result<PanelItemInitData>;
|
|
||||||
|
|
||||||
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>);
|
|
||||||
|
|
||||||
fn close_toplevel(&self);
|
|
||||||
fn auto_size_toplevel(&self);
|
|
||||||
fn set_toplevel_size(&self, size: Vector2<u32>);
|
|
||||||
fn set_toplevel_focused_visuals(&self, focused: bool);
|
|
||||||
|
|
||||||
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>);
|
|
||||||
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool);
|
|
||||||
fn pointer_scroll(
|
|
||||||
&self,
|
|
||||||
surface: &SurfaceID,
|
|
||||||
scroll_distance: Option<Vector2<f32>>,
|
|
||||||
scroll_steps: Option<Vector2<f32>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>);
|
|
||||||
|
|
||||||
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>);
|
|
||||||
fn touch_move(&self, id: u32, position: Vector2<f32>);
|
|
||||||
fn touch_up(&self, id: u32);
|
|
||||||
fn reset_touches(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
|
|
||||||
let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None};
|
|
||||||
Some(panel_item.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PanelItemTrait: Backend + Send + Sync + 'static {
|
|
||||||
fn uid(&self) -> &str;
|
|
||||||
fn serialize_start_data(&self, id: &str) -> Result<Message>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PanelItem<B: Backend + ?Sized> {
|
|
||||||
pub uid: String,
|
|
||||||
node: Weak<Node>,
|
|
||||||
pub backend: Box<B>,
|
|
||||||
}
|
|
||||||
impl<B: Backend + ?Sized> PanelItem<B> {
|
|
||||||
pub fn create(backend: Box<B>, pid: Option<i32>) -> Arc<PanelItem<B>> {
|
|
||||||
debug!(?pid, "Create panel item");
|
|
||||||
|
|
||||||
let startup_settings = pid
|
|
||||||
.and_then(|pid| get_env(pid).ok())
|
|
||||||
.and_then(|env| state(&env));
|
|
||||||
|
|
||||||
let uid = nanoid!();
|
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
|
||||||
.add_to_scenegraph()
|
|
||||||
.unwrap();
|
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
|
||||||
if let Some(startup_settings) = &startup_settings {
|
|
||||||
spatial.set_local_transform(startup_settings.root);
|
|
||||||
}
|
|
||||||
|
|
||||||
let panel_item = Arc::new(PanelItem {
|
|
||||||
uid: uid.clone(),
|
|
||||||
node: Arc::downgrade(&node),
|
|
||||||
backend,
|
|
||||||
});
|
|
||||||
|
|
||||||
let generic_panel_item: Arc<dyn PanelItemTrait> = panel_item.clone();
|
|
||||||
Item::add_to(
|
|
||||||
&node,
|
|
||||||
uid,
|
|
||||||
&ITEM_TYPE_INFO_PANEL,
|
|
||||||
ItemType::Panel(generic_panel_item),
|
|
||||||
);
|
|
||||||
|
|
||||||
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("auto_size_toplevel", Self::auto_size_toplevel_flex);
|
|
||||||
node.add_local_signal(
|
|
||||||
"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_button", Self::pointer_button_flex);
|
|
||||||
node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex);
|
|
||||||
|
|
||||||
node.add_local_signal("keyboard_key", Self::keyboard_keys_flex);
|
|
||||||
|
|
||||||
node.add_local_signal("touch_down", Self::touch_down_flex);
|
|
||||||
node.add_local_signal("touch_move", Self::touch_move_flex);
|
|
||||||
node.add_local_signal("touch_up", Self::touch_up_flex);
|
|
||||||
node.add_local_signal("reset_touches", Self::reset_touches_flex);
|
|
||||||
|
|
||||||
panel_item
|
|
||||||
}
|
|
||||||
pub fn drop_toplevel(&self) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
node.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remote signals
|
|
||||||
impl<B: Backend + ?Sized> PanelItem<B> {
|
|
||||||
pub fn toplevel_parent_changed(&self, parent: &str) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("toplevel_parent_changed", serialize(parent).unwrap());
|
|
||||||
}
|
|
||||||
pub fn toplevel_title_changed(&self, title: &str) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("toplevel_title_changed", serialize(title).unwrap());
|
|
||||||
}
|
|
||||||
pub fn toplevel_app_id_changed(&self, app_id: &str) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap());
|
|
||||||
}
|
|
||||||
pub fn toplevel_fullscreen_active(&self, active: bool) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("toplevel_fullscreen_active", serialize(active).unwrap());
|
|
||||||
}
|
|
||||||
pub fn toplevel_move_request(&self) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
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) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal(
|
|
||||||
"toplevel_resize_request",
|
|
||||||
serialize((up, down, left, right)).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pub fn toplevel_size_changed(&self, size: Vector2<u32>) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("toplevel_size_changed", serialize(size).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursor(&self, geometry: Option<Geometry>) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_child(&self, uid: &str, info: ChildInfo) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("new_child", serialize((uid, info)).unwrap());
|
|
||||||
}
|
|
||||||
pub fn reposition_child(&self, uid: &str, geometry: Geometry) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("reposition_child", serialize((uid, geometry)).unwrap());
|
|
||||||
}
|
|
||||||
pub fn drop_child(&self, uid: &str) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
let _ = node.send_remote_signal("drop_child", serialize(uid).unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Local signals
|
|
||||||
macro_rules! flex_no_args {
|
|
||||||
($fn_name: ident, $trait_fn: ident) => {
|
|
||||||
fn $fn_name(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
panel_item.$trait_fn();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
macro_rules! flex_deserialize {
|
|
||||||
($fn_name: ident, $trait_fn: ident) => {
|
|
||||||
fn $fn_name(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
panel_item.$trait_fn(deserialize(message.as_ref())?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
impl<B: Backend + ?Sized> PanelItem<B> {
|
|
||||||
fn apply_surface_material_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct SurfaceMaterialInfo<'a> {
|
|
||||||
surface: SurfaceID,
|
|
||||||
model_node_path: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
let info: SurfaceMaterialInfo = deserialize(message.as_ref())?;
|
|
||||||
|
|
||||||
let model_node = calling_client
|
|
||||||
.scenegraph
|
|
||||||
.get_node(info.model_node_path)
|
|
||||||
.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")};
|
|
||||||
debug!(?info, "Apply surface material");
|
|
||||||
|
|
||||||
panel_item.apply_surface_material(info.surface, model_part);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
flex_no_args!(close_toplevel_flex, close_toplevel);
|
|
||||||
flex_no_args!(auto_size_toplevel_flex, auto_size_toplevel);
|
|
||||||
flex_deserialize!(set_toplevel_size_changed_flex, set_toplevel_size);
|
|
||||||
|
|
||||||
fn pointer_motion_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
|
|
||||||
let (surface_id, position): (SurfaceID, Vector2<f32>) = deserialize(message.as_ref())?;
|
|
||||||
debug!(?surface_id, ?position, "Pointer deactivate");
|
|
||||||
|
|
||||||
panel_item.pointer_motion(&surface_id, position);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn pointer_button_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
|
|
||||||
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(message.as_ref())?;
|
|
||||||
debug!(?surface_id, button, state, "Pointer button");
|
|
||||||
|
|
||||||
panel_item.pointer_button(&surface_id, button, state != 0);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn pointer_scroll_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct PointerScrollInfo {
|
|
||||||
surface_id: SurfaceID,
|
|
||||||
axis_continuous: Option<Vector2<f32>>,
|
|
||||||
axis_discrete: Option<Vector2<f32>>,
|
|
||||||
}
|
|
||||||
let info: PointerScrollInfo = deserialize(message.as_ref())?;
|
|
||||||
debug!(?info, "Pointer scroll");
|
|
||||||
|
|
||||||
panel_item.pointer_scroll(&info.surface_id, info.axis_continuous, info.axis_discrete);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keyboard_keys_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
|
||||||
let (surface_id, keymap_id, keys): (SurfaceID, &str, Vec<i32>) =
|
|
||||||
deserialize(message.as_ref())?;
|
|
||||||
debug!(?keys, "Set keyboard key state");
|
|
||||||
|
|
||||||
panel_item.keyboard_keys(&surface_id, keymap_id, keys);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
|
|
||||||
let Some(node) = self.node.upgrade() else {return};
|
|
||||||
|
|
||||||
let Ok(message) = serialize(sid) else {return};
|
|
||||||
let _ = node.send_remote_signal("grab_keyboard", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn touch_down_flex(node: &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>) =
|
|
||||||
deserialize(message.as_ref())?;
|
|
||||||
debug!(?surface_id, id, ?position, "Touch down");
|
|
||||||
|
|
||||||
panel_item.touch_down(&surface_id, id, position);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn touch_move_flex(node: &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())?;
|
|
||||||
debug!(?position, "Touch move");
|
|
||||||
|
|
||||||
panel_item.touch_move(id, position);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
flex_deserialize!(touch_up_flex, touch_up);
|
|
||||||
flex_no_args!(reset_touches_flex, reset_touches);
|
|
||||||
}
|
|
||||||
impl<B: Backend + ?Sized> PanelItemTrait for PanelItem<B> {
|
|
||||||
fn uid(&self) -> &str {
|
|
||||||
&self.uid
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_start_data(&self, id: &str) -> Result<Message> {
|
|
||||||
Ok(serialize((id, self.start_data()?))?.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<B: Backend + ?Sized> Backend for PanelItem<B> {
|
|
||||||
fn start_data(&self) -> Result<PanelItemInitData> {
|
|
||||||
self.backend.start_data()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
|
||||||
self.backend.apply_surface_material(surface, model_part)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close_toplevel(&self) {
|
|
||||||
self.backend.close_toplevel()
|
|
||||||
}
|
|
||||||
fn auto_size_toplevel(&self) {
|
|
||||||
self.backend.auto_size_toplevel()
|
|
||||||
}
|
|
||||||
fn set_toplevel_size(&self, size: Vector2<u32>) {
|
|
||||||
self.backend.set_toplevel_size(size)
|
|
||||||
}
|
|
||||||
fn set_toplevel_focused_visuals(&self, focused: bool) {
|
|
||||||
self.backend.set_toplevel_focused_visuals(focused)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
|
||||||
self.backend.pointer_motion(surface, position)
|
|
||||||
}
|
|
||||||
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
|
||||||
self.backend.pointer_button(surface, button, pressed)
|
|
||||||
}
|
|
||||||
fn pointer_scroll(
|
|
||||||
&self,
|
|
||||||
surface: &SurfaceID,
|
|
||||||
scroll_distance: Option<Vector2<f32>>,
|
|
||||||
scroll_steps: Option<Vector2<f32>>,
|
|
||||||
) {
|
|
||||||
self.backend
|
|
||||||
.pointer_scroll(surface, scroll_distance, scroll_steps)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) {
|
|
||||||
self.backend.keyboard_keys(surface, keymap_id, keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) {
|
|
||||||
self.backend.touch_down(surface, id, position)
|
|
||||||
}
|
|
||||||
fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
|
||||||
self.backend.touch_move(id, position)
|
|
||||||
}
|
|
||||||
fn touch_up(&self, id: u32) {
|
|
||||||
self.backend.touch_up(id)
|
|
||||||
}
|
|
||||||
fn reset_touches(&self) {
|
|
||||||
self.backend.reset_touches()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<B: Backend + ?Sized> Drop for PanelItem<B> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Dropped panel item, basically just a debug breakpoint place
|
|
||||||
}
|
|
||||||
}
|
|
||||||
173
src/nodes/mod.rs
173
src/nodes/mod.rs
@@ -8,25 +8,26 @@ pub mod input;
|
|||||||
pub mod items;
|
pub mod items;
|
||||||
pub mod root;
|
pub mod root;
|
||||||
pub mod spatial;
|
pub mod spatial;
|
||||||
|
pub mod startup;
|
||||||
|
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use core::hash::BuildHasherDefault;
|
||||||
|
use dashmap::DashMap;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
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::FxHasher;
|
||||||
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;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::future::Future;
|
|
||||||
use std::os::fd::OwnedFd;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use tracing::{debug_span, instrument};
|
||||||
|
|
||||||
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 self::alias::Alias;
|
use self::alias::Alias;
|
||||||
use self::audio::Sound;
|
use self::audio::Sound;
|
||||||
@@ -37,28 +38,10 @@ use self::input::{InputHandler, InputMethod};
|
|||||||
use self::items::{Item, ItemAcceptor, ItemUI};
|
use self::items::{Item, ItemAcceptor, ItemUI};
|
||||||
use self::spatial::zone::Zone;
|
use self::spatial::zone::Zone;
|
||||||
use self::spatial::Spatial;
|
use self::spatial::Spatial;
|
||||||
|
use self::startup::StartupSettings;
|
||||||
|
|
||||||
#[derive(Default)]
|
pub type Signal = fn(&Node, Arc<Client>, &[u8]) -> Result<()>;
|
||||||
pub struct Message {
|
pub type Method = fn(&Node, Arc<Client>, &[u8]) -> Result<Vec<u8>>;
|
||||||
pub data: Vec<u8>,
|
|
||||||
pub fds: Vec<OwnedFd>,
|
|
||||||
}
|
|
||||||
impl From<Vec<u8>> for Message {
|
|
||||||
fn from(data: Vec<u8>) -> Self {
|
|
||||||
Message {
|
|
||||||
data,
|
|
||||||
fds: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsRef<[u8]> for Message {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Signal = fn(&Node, Arc<Client>, Message) -> Result<()>;
|
|
||||||
pub type Method = fn(&Node, Arc<Client>, Message, MethodResponseSender);
|
|
||||||
|
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub enabled: Arc<AtomicBool>,
|
pub enabled: Arc<AtomicBool>,
|
||||||
@@ -67,8 +50,8 @@ pub struct Node {
|
|||||||
client: Weak<Client>,
|
client: Weak<Client>,
|
||||||
message_sender_handle: Option<MessageSenderHandle>,
|
message_sender_handle: Option<MessageSenderHandle>,
|
||||||
// trailing_slash_pos: usize,
|
// trailing_slash_pos: usize,
|
||||||
local_signals: Mutex<FxHashMap<String, Signal>>,
|
local_signals: DashMap<String, Signal, BuildHasherDefault<FxHasher>>,
|
||||||
local_methods: Mutex<FxHashMap<String, Method>>,
|
local_methods: DashMap<String, Method, BuildHasherDefault<FxHasher>>,
|
||||||
destroyable: bool,
|
destroyable: bool,
|
||||||
|
|
||||||
pub alias: OnceCell<Arc<Alias>>,
|
pub alias: OnceCell<Arc<Alias>>,
|
||||||
@@ -96,6 +79,9 @@ pub struct Node {
|
|||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
pub sound: OnceCell<Arc<Sound>>,
|
pub sound: OnceCell<Arc<Sound>>,
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
pub startup_settings: OnceCell<Mutex<StartupSettings>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
@@ -139,6 +125,7 @@ impl Node {
|
|||||||
item_acceptor: OnceCell::new(),
|
item_acceptor: OnceCell::new(),
|
||||||
item_ui: OnceCell::new(),
|
item_ui: OnceCell::new(),
|
||||||
sound: OnceCell::new(),
|
sound: OnceCell::new(),
|
||||||
|
startup_settings: OnceCell::new(),
|
||||||
};
|
};
|
||||||
node.add_local_signal("set_enabled", Node::set_enabled_flex);
|
node.add_local_signal("set_enabled", Node::set_enabled_flex);
|
||||||
node.add_local_signal("destroy", Node::destroy_flex);
|
node.add_local_signal("destroy", Node::destroy_flex);
|
||||||
@@ -157,33 +144,11 @@ impl Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_enabled_flex(
|
pub fn set_enabled_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
node.enabled.store(deserialize(data)?, Ordering::Relaxed);
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
node.enabled
|
|
||||||
.store(deserialize(message.as_ref())?, Ordering::Relaxed);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// very much up for debate if we should allow this, as you can match objects using this
|
pub fn destroy_flex(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
|
||||||
// pub fn get_client_pid_flex(
|
|
||||||
// node: &Node,
|
|
||||||
// _calling_client: Arc<Client>,
|
|
||||||
// _message: Message,
|
|
||||||
// ) -> Result<Message> {
|
|
||||||
// let client = node
|
|
||||||
// .client
|
|
||||||
// .upgrade()
|
|
||||||
// .ok_or_else(|| eyre!("Could not get client for node?"))?;
|
|
||||||
// let pid = client.pid.ok_or_else(|| eyre!("Client PID is unknown"))?;
|
|
||||||
// Ok(serialize(pid)?.into())
|
|
||||||
// }
|
|
||||||
pub fn destroy_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
_message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
if node.destroyable {
|
if node.destroyable {
|
||||||
node.destroy();
|
node.destroy();
|
||||||
}
|
}
|
||||||
@@ -191,10 +156,10 @@ impl Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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.insert(name.to_string(), signal);
|
||||||
}
|
}
|
||||||
pub fn add_local_method(&self, name: &str, method: Method) {
|
pub fn add_local_method(&self, name: &str, method: Method) {
|
||||||
self.local_methods.lock().insert(name.to_string(), method);
|
self.local_methods.insert(name.to_string(), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_aspect<F, T>(
|
pub fn get_aspect<F, T>(
|
||||||
@@ -215,7 +180,7 @@ impl Node {
|
|||||||
&self,
|
&self,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
method: &str,
|
method: &str,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<(), ScenegraphError> {
|
) -> Result<(), ScenegraphError> {
|
||||||
if let Some(alias) = self.alias.get() {
|
if let Some(alias) = self.alias.get() {
|
||||||
if !alias.info.server_signals.iter().any(|e| e == &method) {
|
if !alias.info.server_signals.iter().any(|e| e == &method) {
|
||||||
@@ -225,15 +190,13 @@ impl Node {
|
|||||||
.original
|
.original
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or(ScenegraphError::BrokenAlias)?
|
.ok_or(ScenegraphError::BrokenAlias)?
|
||||||
.send_local_signal(calling_client, method, message)
|
.send_local_signal(calling_client, method, data)
|
||||||
} else {
|
} else {
|
||||||
let signal = self
|
let signal = self
|
||||||
.local_signals
|
.local_signals
|
||||||
.lock()
|
|
||||||
.get(method)
|
.get(method)
|
||||||
.cloned()
|
|
||||||
.ok_or(ScenegraphError::SignalNotFound)?;
|
.ok_or(ScenegraphError::SignalNotFound)?;
|
||||||
signal(self, calling_client, message).map_err(|error| ScenegraphError::SignalError {
|
signal(self, calling_client, data).map_err(|error| ScenegraphError::SignalError {
|
||||||
error: error.to_string(),
|
error: error.to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -242,83 +205,63 @@ impl Node {
|
|||||||
&self,
|
&self,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
method: &str,
|
method: &str,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>, ScenegraphError> {
|
||||||
) {
|
|
||||||
if let Some(alias) = self.alias.get() {
|
if let Some(alias) = self.alias.get() {
|
||||||
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));
|
return Err(ScenegraphError::MethodNotFound);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
let Some(alias) = alias.original.upgrade() else {
|
alias
|
||||||
response.send(Err(ScenegraphError::BrokenAlias));
|
.original
|
||||||
return;
|
.upgrade()
|
||||||
};
|
.ok_or(ScenegraphError::BrokenAlias)?
|
||||||
alias.execute_local_method(
|
.execute_local_method(calling_client, method, data)
|
||||||
calling_client,
|
|
||||||
method,
|
|
||||||
Message {
|
|
||||||
data: message.data.clone(),
|
|
||||||
fds: Vec::new(),
|
|
||||||
},
|
|
||||||
response,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
let Some(method) = self.local_methods.lock().get(method).cloned() else {
|
let method = self
|
||||||
response.send(Err(ScenegraphError::MethodNotFound));
|
.local_methods
|
||||||
return;
|
.get(method)
|
||||||
};
|
.ok_or(ScenegraphError::MethodNotFound)?;
|
||||||
method(self, calling_client, message, response);
|
|
||||||
|
debug_span!("Handle method").in_scope(|| {
|
||||||
|
method(self, calling_client, data).map_err(|error| ScenegraphError::MethodError {
|
||||||
|
error: error.to_string(),
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn send_remote_signal(&self, method: &str, message: impl Into<Message>) -> Result<()> {
|
#[instrument(level = "debug", skip_all)]
|
||||||
let message = message.into();
|
pub fn send_remote_signal(&self, method: &str, data: &[u8]) -> Result<()> {
|
||||||
self.aliases
|
self.aliases
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|alias| alias.info.client_signals.iter().any(|e| e == &method))
|
.filter(|alias| alias.info.client_signals.iter().any(|e| e == &method))
|
||||||
.filter_map(|alias| alias.node.upgrade())
|
.filter_map(|alias| alias.node.upgrade())
|
||||||
.for_each(|node| {
|
.for_each(|node| {
|
||||||
// Beware! file descriptors will not be sent to aliases!!!
|
let _ = node.send_remote_signal(method, data);
|
||||||
let _ = node.send_remote_signal(
|
|
||||||
method,
|
|
||||||
Message {
|
|
||||||
data: message.data.clone(),
|
|
||||||
fds: Vec::new(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
let path = self.path.clone();
|
let path = self.path.clone();
|
||||||
let method = method.to_string();
|
let method = method.to_string();
|
||||||
|
let data = data.to_vec();
|
||||||
if let Some(handle) = self.message_sender_handle.as_ref() {
|
if let Some(handle) = self.message_sender_handle.as_ref() {
|
||||||
handle.signal(path.as_str(), method.as_str(), &message.data, message.fds)?;
|
handle.signal(path.as_str(), method.as_str(), data.as_slice())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn execute_remote_method(
|
// #[instrument(level = "debug", skip_all)]
|
||||||
&self,
|
// pub fn execute_remote_method(
|
||||||
method: &str,
|
// &self,
|
||||||
message: impl Into<Message>,
|
// method: &str,
|
||||||
) -> Result<impl Future<Output = Result<Message>>> {
|
// data: Vec<u8>,
|
||||||
let message = message.into();
|
// ) -> Result<impl Future<Output = Result<Vec<u8>>>> {
|
||||||
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 future = message_sender_handle.method(self.path.as_str(), method, &data)?;
|
||||||
message_sender_handle.method(self.path.as_str(), method, &message.data, message.fds)?;
|
|
||||||
|
|
||||||
Ok(async {
|
// Ok(async { future.await.map_err(|e| eyre!(e)) })
|
||||||
match future.await {
|
// }
|
||||||
Ok(m) => {
|
|
||||||
let (data, fds) = m.into_components();
|
|
||||||
Ok(Message { data, fds })
|
|
||||||
}
|
|
||||||
Err(e) => Err(eyre!(e)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl Debug for Node {
|
impl Debug for Node {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
use super::spatial::Spatial;
|
use super::spatial::Spatial;
|
||||||
use super::{Message, Node};
|
use super::Node;
|
||||||
use crate::core::client::Client;
|
use crate::core::client::Client;
|
||||||
use crate::core::client_state::{ClientState, ClientStateInternal};
|
|
||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::core::scenegraph::MethodResponseSender;
|
|
||||||
use crate::wayland::WAYLAND_DISPLAY;
|
|
||||||
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 stardust_xr::schemas::flex::{deserialize, serialize};
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
use tracing::instrument;
|
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;
|
||||||
|
|
||||||
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
||||||
|
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
pub node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
send_frame_event: AtomicBool,
|
send_frame_event: AtomicBool,
|
||||||
}
|
}
|
||||||
impl Root {
|
impl Root {
|
||||||
@@ -27,13 +21,17 @@ impl Root {
|
|||||||
let node = Node::create(client, "", "", false);
|
let node = Node::create(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(
|
|
||||||
"get_connection_environment",
|
|
||||||
get_connection_environment_flex,
|
|
||||||
);
|
|
||||||
let node = node.add_to_scenegraph()?;
|
let node = node.add_to_scenegraph()?;
|
||||||
let _ = Spatial::add_to(&node, None, client.state.root, false);
|
let _ = Spatial::add_to(
|
||||||
|
&node,
|
||||||
|
None,
|
||||||
|
client
|
||||||
|
.startup_settings
|
||||||
|
.as_ref()
|
||||||
|
.map(|settings| settings.transform)
|
||||||
|
.unwrap_or(Mat4::IDENTITY),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(ROOT_REGISTRY.add(Root {
|
Ok(ROOT_REGISTRY.add(Root {
|
||||||
node,
|
node,
|
||||||
@@ -41,11 +39,7 @@ impl Root {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe_frame_flex(
|
fn subscribe_frame_flex(_node: &Node, calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
_message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
calling_client
|
calling_client
|
||||||
.root
|
.root
|
||||||
.get()
|
.get()
|
||||||
@@ -60,7 +54,7 @@ impl Root {
|
|||||||
if let Ok(data) = serialize((delta, 0.0)) {
|
if let Ok(data) = serialize((delta, 0.0)) {
|
||||||
for root in ROOT_REGISTRY.get_valid_contents() {
|
for root in ROOT_REGISTRY.get_valid_contents() {
|
||||||
if root.send_frame_event.load(Ordering::Relaxed) {
|
if root.send_frame_event.load(Ordering::Relaxed) {
|
||||||
let _ = root.node.send_remote_signal("frame", data.clone());
|
let _ = root.node.send_remote_signal("frame", &data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,36 +63,11 @@ impl Root {
|
|||||||
fn set_base_prefixes_flex(
|
fn set_base_prefixes_flex(
|
||||||
_node: &Node,
|
_node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
*calling_client.base_resource_prefixes.lock() = deserialize(message.as_ref())?;
|
*calling_client.base_resource_prefixes.lock() = deserialize(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_token_flex(
|
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(|| {
|
|
||||||
let state: ClientStateInternal = deserialize(message.as_ref())?;
|
|
||||||
let token = ClientState::from_deserialized(&calling_client, state).token();
|
|
||||||
Ok(serialize(token)?.into())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_transform(&self, transform: Mat4) {
|
|
||||||
let spatial = self.node.spatial.get().unwrap();
|
|
||||||
spatial.set_spatial_parent(None).unwrap();
|
|
||||||
spatial.set_local_transform(transform);
|
|
||||||
}
|
|
||||||
pub fn save_state(&self) -> impl Future<Output = Result<ClientStateInternal>> {
|
|
||||||
let future = self
|
|
||||||
.node
|
|
||||||
.execute_remote_method("save_state", Message::default());
|
|
||||||
async move { Ok(deserialize(&future?.await?.data)?) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Root {
|
impl Drop for Root {
|
||||||
@@ -106,33 +75,3 @@ impl Drop for Root {
|
|||||||
ROOT_REGISTRY.remove(self);
|
ROOT_REGISTRY.remove(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! var_env_insert {
|
|
||||||
($env:ident, $name:ident) => {
|
|
||||||
$env.insert(stringify!($name).to_string(), $name.get().unwrap().clone());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub fn get_connection_environment_flex(
|
|
||||||
_node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
_message: Message,
|
|
||||||
response: MethodResponseSender,
|
|
||||||
) {
|
|
||||||
response.wrap_sync(move || {
|
|
||||||
let mut env: FxHashMap<String, String> = FxHashMap::default();
|
|
||||||
var_env_insert!(env, STARDUST_INSTANCE);
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
{
|
|
||||||
var_env_insert!(env, WAYLAND_DISPLAY);
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
var_env_insert!(env, DISPLAY);
|
|
||||||
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
|
||||||
env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string());
|
|
||||||
env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
|
|
||||||
env.insert("CLUTTER_BACKEND".to_string(), "wayland".to_string());
|
|
||||||
env.insert("SDL_VIDEODRIVER".to_string(), "wayland".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(serialize(env)?.into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
pub mod zone;
|
pub mod zone;
|
||||||
|
|
||||||
use self::zone::{create_zone_flex, Zone};
|
use self::zone::{create_zone_flex, Zone};
|
||||||
use super::{Message, Node};
|
use super::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 color_eyre::eyre::{ensure, eyre, Result};
|
use color_eyre::eyre::{ensure, eyre, Result};
|
||||||
use glam::{vec3a, Mat4, Quat};
|
use glam::{vec3a, Mat4, Quat};
|
||||||
use mint::Vector3;
|
use mint::Vector3;
|
||||||
@@ -17,6 +16,7 @@ use std::fmt::Debug;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::{Arc, OnceLock, Weak};
|
use std::sync::{Arc, OnceLock, Weak};
|
||||||
use stereokit::{bounds_grow_to_fit_box, Bounds};
|
use stereokit::{bounds_grow_to_fit_box, Bounds};
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
static ZONEABLE_REGISTRY: Registry<Spatial> = Registry::new();
|
static ZONEABLE_REGISTRY: Registry<Spatial> = Registry::new();
|
||||||
|
|
||||||
@@ -80,6 +80,7 @@ impl Spatial {
|
|||||||
self.node.upgrade()
|
self.node.upgrade()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn space_to_space_matrix(from: Option<&Spatial>, to: Option<&Spatial>) -> Mat4 {
|
pub fn space_to_space_matrix(from: Option<&Spatial>, to: Option<&Spatial>) -> Mat4 {
|
||||||
let space_to_world_matrix = from.map_or(Mat4::IDENTITY, |from| from.global_transform());
|
let space_to_world_matrix = from.map_or(Mat4::IDENTITY, |from| from.global_transform());
|
||||||
let world_to_space_matrix = to.map_or(Mat4::IDENTITY, |to| to.global_transform().inverse());
|
let world_to_space_matrix = to.map_or(Mat4::IDENTITY, |to| to.global_transform().inverse());
|
||||||
@@ -87,6 +88,7 @@ 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
|
||||||
|
#[instrument(level = "debug")]
|
||||||
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
|
||||||
@@ -104,6 +106,7 @@ impl Spatial {
|
|||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn local_transform(&self) -> Mat4 {
|
pub fn local_transform(&self) -> Mat4 {
|
||||||
*self.transform.lock()
|
*self.transform.lock()
|
||||||
}
|
}
|
||||||
@@ -113,9 +116,11 @@ impl Spatial {
|
|||||||
None => *self.transform.lock(),
|
None => *self.transform.lock(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[instrument]
|
||||||
pub fn set_local_transform(&self, transform: Mat4) {
|
pub fn set_local_transform(&self, transform: Mat4) {
|
||||||
*self.transform.lock() = transform;
|
*self.transform.lock() = transform;
|
||||||
}
|
}
|
||||||
|
#[instrument(level = "debug", skip(self, reference_space))]
|
||||||
pub fn set_local_transform_components(
|
pub fn set_local_transform_components(
|
||||||
&self,
|
&self,
|
||||||
reference_space: Option<&Spatial>,
|
reference_space: Option<&Spatial>,
|
||||||
@@ -159,6 +164,7 @@ impl Spatial {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn is_ancestor_of(&self, spatial: Arc<Spatial>) -> bool {
|
pub fn is_ancestor_of(&self, spatial: Arc<Spatial>) -> bool {
|
||||||
let mut current_ancestor = spatial;
|
let mut current_ancestor = spatial;
|
||||||
loop {
|
loop {
|
||||||
@@ -190,6 +196,7 @@ impl Spatial {
|
|||||||
*self.parent.lock() = new_parent;
|
*self.parent.lock() = new_parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn set_spatial_parent(&self, parent: Option<Arc<Spatial>>) -> Result<()> {
|
pub fn set_spatial_parent(&self, parent: Option<Arc<Spatial>>) -> Result<()> {
|
||||||
let is_ancestor = parent
|
let is_ancestor = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -203,6 +210,7 @@ impl Spatial {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn set_spatial_parent_in_place(&self, parent: Option<Arc<Spatial>>) -> Result<()> {
|
pub fn set_spatial_parent_in_place(&self, parent: Option<Arc<Spatial>>) -> Result<()> {
|
||||||
let is_ancestor = parent
|
let is_ancestor = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -224,84 +232,72 @@ impl Spatial {
|
|||||||
pub fn get_bounding_box_flex(
|
pub fn get_bounding_box_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>> {
|
||||||
) {
|
let this_spatial = node
|
||||||
response.wrap_sync(move || {
|
.spatial
|
||||||
let this_spatial = node
|
.get()
|
||||||
.spatial
|
.ok_or_else(|| eyre!("Node doesn't have a spatial?"))?;
|
||||||
.get()
|
let relative_spatial_path: Option<&str> = deserialize(data)?;
|
||||||
.ok_or_else(|| eyre!("Node doesn't have a spatial?"))?;
|
let bounds = if let Some(relative_spatial_path) = relative_spatial_path {
|
||||||
let relative_spatial_path: Option<&str> = deserialize(message.as_ref())?;
|
let relative_spatial = find_reference_space(&calling_client, relative_spatial_path)?;
|
||||||
let bounds = if let Some(relative_spatial_path) = relative_spatial_path {
|
let center =
|
||||||
let relative_spatial =
|
Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial))
|
||||||
find_reference_space(&calling_client, relative_spatial_path)?;
|
.transform_point3([0.0; 3].into());
|
||||||
let center =
|
let bounds: Bounds = Bounds {
|
||||||
Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial))
|
center,
|
||||||
.transform_point3([0.0; 3].into());
|
dimensions: [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()
|
|
||||||
};
|
};
|
||||||
|
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((
|
serialize((
|
||||||
mint::Vector3::from(bounds.center),
|
mint::Vector3::from(bounds.center),
|
||||||
mint::Vector3::from(bounds.dimensions),
|
mint::Vector3::from(bounds.dimensions),
|
||||||
))?
|
))
|
||||||
.into())
|
.map_err(|e| e.into())
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_transform_flex(
|
pub fn get_transform_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>> {
|
||||||
) {
|
let this_spatial = node
|
||||||
response.wrap_sync(move || {
|
.spatial
|
||||||
let this_spatial = node
|
.get()
|
||||||
.spatial
|
.ok_or_else(|| eyre!("Node doesn't have a spatial?"))?;
|
||||||
.get()
|
let relative_spatial = find_reference_space(&calling_client, deserialize(data)?)?;
|
||||||
.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(
|
let (scale, rotation, position) = Spatial::space_to_space_matrix(
|
||||||
Some(this_spatial.as_ref()),
|
Some(this_spatial.as_ref()),
|
||||||
Some(relative_spatial.as_ref()),
|
Some(relative_spatial.as_ref()),
|
||||||
)
|
)
|
||||||
.to_scale_rotation_translation();
|
.to_scale_rotation_translation();
|
||||||
|
|
||||||
Ok(serialize((
|
serialize((
|
||||||
mint::Vector3::from(position),
|
mint::Vector3::from(position),
|
||||||
mint::Quaternion::from(rotation),
|
mint::Quaternion::from(rotation),
|
||||||
mint::Vector3::from(scale),
|
mint::Vector3::from(scale),
|
||||||
))?
|
))
|
||||||
.into())
|
.map_err(|e| e.into())
|
||||||
});
|
|
||||||
}
|
}
|
||||||
pub fn set_transform_flex(
|
pub fn set_transform_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct TransformArgs<'a> {
|
struct TransformArgs<'a> {
|
||||||
reference_space_path: Option<&'a str>,
|
reference_space_path: Option<&'a str>,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
}
|
}
|
||||||
let transform_args: TransformArgs = deserialize(message.as_ref())?;
|
let transform_args: TransformArgs = deserialize(data)?;
|
||||||
let reference_space_transform = transform_args
|
let reference_space_transform = transform_args
|
||||||
.reference_space_path
|
.reference_space_path
|
||||||
.map(|path| find_reference_space(&calling_client, path))
|
.map(|path| find_reference_space(&calling_client, path))
|
||||||
@@ -316,29 +312,25 @@ impl Spatial {
|
|||||||
pub fn set_spatial_parent_flex(
|
pub fn set_spatial_parent_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let parent = find_spatial_parent(&calling_client, deserialize(message.as_ref())?)?;
|
let parent = find_spatial_parent(&calling_client, deserialize(data)?)?;
|
||||||
node.spatial.get().unwrap().set_spatial_parent(Some(parent))
|
node.spatial.get().unwrap().set_spatial_parent(Some(parent))
|
||||||
}
|
}
|
||||||
pub fn set_spatial_parent_in_place_flex(
|
pub fn set_spatial_parent_in_place_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let parent = find_spatial_parent(&calling_client, deserialize(message.as_ref())?)?;
|
let parent = find_spatial_parent(&calling_client, deserialize(data)?)?;
|
||||||
node.spatial
|
node.spatial
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_spatial_parent_in_place(Some(parent))?;
|
.set_spatial_parent_in_place(Some(parent))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn set_zoneable_flex(
|
pub fn set_zoneable_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
node: &Node,
|
let zoneable: bool = deserialize(data)?;
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
let zoneable: bool = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
let spatial = node.spatial.get().unwrap();
|
||||||
if zoneable {
|
if zoneable {
|
||||||
ZONEABLE_REGISTRY.add_raw(spatial);
|
ZONEABLE_REGISTRY.add_raw(spatial);
|
||||||
@@ -352,82 +344,74 @@ impl Spatial {
|
|||||||
pub fn field_distance_flex(
|
pub fn field_distance_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>> {
|
||||||
) {
|
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(data)?;
|
||||||
response.wrap_sync(move || {
|
let spatial = node.spatial.get().unwrap();
|
||||||
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
|
|
||||||
let output = fields
|
let output = fields
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
calling_client
|
calling_client
|
||||||
.get_node("Field", f?)
|
.get_node("Field", f?)
|
||||||
.ok()?
|
.ok()?
|
||||||
.get_aspect("Field", "field", |n| &n.field)
|
.get_aspect("Field", "field", |n| &n.field)
|
||||||
.ok()
|
.ok()
|
||||||
.cloned()
|
.cloned()
|
||||||
})
|
})
|
||||||
.map(|f| f.map(|f| f.distance(spatial, point.into())))
|
.map(|f| f.map(|f| f.distance(spatial, point.into())))
|
||||||
.collect::<Vec<Option<f32>>>();
|
.collect::<Vec<Option<f32>>>();
|
||||||
|
|
||||||
Ok(serialize(output)?.into())
|
Ok(serialize(output)?)
|
||||||
});
|
|
||||||
}
|
}
|
||||||
pub fn field_normal_flex(
|
pub fn field_normal_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>> {
|
||||||
) {
|
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(data)?;
|
||||||
response.wrap_sync(move || {
|
let spatial = node.spatial.get().unwrap();
|
||||||
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
|
|
||||||
let output = fields
|
let output = fields
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
calling_client
|
calling_client
|
||||||
.get_node("Field", f?)
|
.get_node("Field", f?)
|
||||||
.ok()?
|
.ok()?
|
||||||
.get_aspect("Field", "field", |n| &n.field)
|
.get_aspect("Field", "field", |n| &n.field)
|
||||||
.ok()
|
.ok()
|
||||||
.cloned()
|
.cloned()
|
||||||
})
|
})
|
||||||
.map(|f| f.map(|f| Vector3::from(f.normal(spatial, point.into(), 0.001))))
|
.map(|f| f.map(|f| Vector3::from(f.normal(spatial, point.into(), 0.001))))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(serialize(output)?.into())
|
Ok(serialize(output)?)
|
||||||
});
|
|
||||||
}
|
}
|
||||||
pub fn field_closest_point_flex(
|
pub fn field_closest_point_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
calling_client: Arc<Client>,
|
calling_client: Arc<Client>,
|
||||||
message: Message,
|
data: &[u8],
|
||||||
response: MethodResponseSender,
|
) -> Result<Vec<u8>> {
|
||||||
) {
|
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(data)?;
|
||||||
response.wrap_sync(move || {
|
let spatial = node.spatial.get().unwrap();
|
||||||
let (point, fields): (Vector3<f32>, Vec<Option<&str>>) = deserialize(message.as_ref())?;
|
|
||||||
let spatial = node.spatial.get().unwrap();
|
|
||||||
|
|
||||||
let output = fields
|
let output = fields
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
calling_client
|
calling_client
|
||||||
.get_node("Field", f?)
|
.get_node("Field", f?)
|
||||||
.ok()?
|
.ok()?
|
||||||
.get_aspect("Field", "field", |n| &n.field)
|
.get_aspect("Field", "field", |n| &n.field)
|
||||||
.ok()
|
.ok()
|
||||||
.cloned()
|
.cloned()
|
||||||
})
|
})
|
||||||
.map(|f| f.map(|f| Vector3::from(f.closest_point(spatial, point.into(), 0.001))))
|
.map(|f| f.map(|f| Vector3::from(f.closest_point(spatial, point.into(), 0.001))))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(serialize(output)?.into())
|
Ok(serialize(output)?)
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
pub(self) fn zone_distance(&self) -> f32 {
|
pub(self) fn zone_distance(&self) -> f32 {
|
||||||
self.zone
|
self.zone
|
||||||
.lock()
|
.lock()
|
||||||
@@ -506,11 +490,7 @@ pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
|||||||
node.add_to_scenegraph().map(|_| ())
|
node.add_to_scenegraph().map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_spatial_flex(
|
pub fn create_spatial_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
_node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
message: Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateSpatialInfo<'a> {
|
struct CreateSpatialInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -518,7 +498,7 @@ pub fn create_spatial_flex(
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
zoneable: bool,
|
zoneable: bool,
|
||||||
}
|
}
|
||||||
let info: CreateSpatialInfo = deserialize(message.as_ref())?;
|
let info: CreateSpatialInfo = deserialize(data)?;
|
||||||
let node = Node::create(&calling_client, "/spatial/spatial", info.name, true);
|
let node = Node::create(&calling_client, "/spatial/spatial", info.name, true);
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
let transform = parse_transform(info.transform, true, true, true);
|
let transform = parse_transform(info.transform, true, true, true);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
alias::{Alias, AliasInfo},
|
alias::{Alias, AliasInfo},
|
||||||
fields::{find_field, Field},
|
fields::{find_field, Field},
|
||||||
spatial::{find_spatial_parent, parse_transform},
|
spatial::{find_spatial_parent, parse_transform},
|
||||||
Message, Node,
|
Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
@@ -31,19 +31,17 @@ 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 node = zone.spatial.node.upgrade().unwrap();
|
||||||
let Ok(message) = serialize(&spatial.uid) else {return};
|
let _ = node.send_remote_signal("capture", &serialize(&spatial.uid).unwrap());
|
||||||
let _ = node.send_remote_signal("capture", message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn release(spatial: &Spatial) {
|
pub fn release(spatial: &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());
|
||||||
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 node = spatial_zone.spatial.node.upgrade().unwrap();
|
||||||
spatial_zone.captured.remove(spatial);
|
spatial_zone.captured.remove(spatial);
|
||||||
let Ok(message) = serialize(&spatial.uid) else {return};
|
let _ = node.send_remote_signal("release", &serialize(&spatial.uid).unwrap());
|
||||||
let _ = node.send_remote_signal("release", message);
|
|
||||||
}
|
}
|
||||||
*spatial_zone = Weak::new();
|
*spatial_zone = Weak::new();
|
||||||
}
|
}
|
||||||
@@ -68,20 +66,20 @@ impl Zone {
|
|||||||
let _ = node.zone.set(zone.clone());
|
let _ = node.zone.set(zone.clone());
|
||||||
zone
|
zone
|
||||||
}
|
}
|
||||||
fn capture_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn capture_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
let zone = node.zone.get().unwrap();
|
let zone = node.zone.get().unwrap();
|
||||||
let capture_path: &str = deserialize(message.as_ref())?;
|
let capture_path: &str = deserialize(data)?;
|
||||||
let spatial = find_spatial(&calling_client, "Spatial", capture_path)?;
|
let spatial = find_spatial(&calling_client, "Spatial", capture_path)?;
|
||||||
capture(&spatial, zone);
|
capture(&spatial, zone);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn release_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
fn release_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
let capture_path: &str = deserialize(message.as_ref())?;
|
let capture_path: &str = deserialize(data)?;
|
||||||
let spatial = find_spatial(&calling_client, "Spatial", capture_path)?;
|
let spatial = find_spatial(&calling_client, "Spatial", capture_path)?;
|
||||||
release(&spatial);
|
release(&spatial);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn update(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
|
fn update(node: &Node, _calling_client: Arc<Client>, _data: &[u8]) -> Result<()> {
|
||||||
let zone = node.zone.get().unwrap();
|
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(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
|
||||||
@@ -131,10 +129,10 @@ 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 entered_uid in zoneables.keys().filter(|k| !old_zoneables.contains_key(*k)) {
|
||||||
node.send_remote_signal("enter", serialize(entered_uid)?)?;
|
node.send_remote_signal("enter", &serialize(entered_uid)?)?;
|
||||||
}
|
}
|
||||||
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)?)?;
|
node.send_remote_signal("leave", &serialize(left_uid)?)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
*old_zoneables = zoneables;
|
*old_zoneables = zoneables;
|
||||||
@@ -150,7 +148,7 @@ impl Drop for Zone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_zone_flex(_node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
pub fn create_zone_flex(_node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CreateZoneInfo<'a> {
|
struct CreateZoneInfo<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@@ -158,7 +156,7 @@ pub fn create_zone_flex(_node: &Node, calling_client: Arc<Client>, message: Mess
|
|||||||
transform: Transform,
|
transform: Transform,
|
||||||
field_path: &'a str,
|
field_path: &'a str,
|
||||||
}
|
}
|
||||||
let info: CreateZoneInfo = deserialize(message.as_ref())?;
|
let info: CreateZoneInfo = deserialize(data)?;
|
||||||
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
let parent = find_spatial_parent(&calling_client, info.parent_path)?;
|
||||||
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)?;
|
||||||
|
|||||||
146
src/nodes/startup.rs
Normal file
146
src/nodes/startup.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
use crate::{core::client::Client, wayland::WAYLAND_DISPLAY, STARDUST_INSTANCE};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
items::{ItemAcceptor, TypeInfo},
|
||||||
|
spatial::find_spatial,
|
||||||
|
Node,
|
||||||
|
};
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use glam::Mat4;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref STARTUP_SETTINGS: Mutex<FxHashMap<String, StartupSettings>> = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct StartupSettings {
|
||||||
|
pub transform: Mat4,
|
||||||
|
pub acceptors: FxHashMap<&'static TypeInfo, Weak<ItemAcceptor>>,
|
||||||
|
}
|
||||||
|
impl StartupSettings {
|
||||||
|
pub fn add_to(node: &Arc<Node>) {
|
||||||
|
let _ = node
|
||||||
|
.startup_settings
|
||||||
|
.set(Mutex::new(StartupSettings::default()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_root_flex(node: &Node, calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let spatial = find_spatial(&calling_client, "Root spatial", deserialize(data)?)?;
|
||||||
|
node.startup_settings.get().unwrap().lock().transform = spatial.global_transform();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_automatic_acceptor_flex(
|
||||||
|
node: &Node,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let acceptor_node = calling_client.get_node("Item acceptor", deserialize(data)?)?;
|
||||||
|
let acceptor =
|
||||||
|
acceptor_node.get_aspect("Item acceptor", "item acceptor", |n| &n.item_acceptor)?;
|
||||||
|
let mut startup_settings = node.startup_settings.get().unwrap().lock();
|
||||||
|
startup_settings
|
||||||
|
.acceptors
|
||||||
|
.insert(acceptor.type_info, Arc::downgrade(acceptor));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_startup_token_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
_data: &[u8],
|
||||||
|
) -> Result<Vec<u8>> {
|
||||||
|
let id = nanoid::nanoid!();
|
||||||
|
let data = serialize(&id)?;
|
||||||
|
STARTUP_SETTINGS
|
||||||
|
.lock()
|
||||||
|
.insert(id, node.startup_settings.get().unwrap().lock().clone());
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Debug for StartupSettings {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("StartupSettings")
|
||||||
|
.field("transform", &self.transform)
|
||||||
|
.field(
|
||||||
|
"acceptors",
|
||||||
|
&self
|
||||||
|
.acceptors
|
||||||
|
.iter()
|
||||||
|
.map(|(k, _)| k.type_name)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
||||||
|
let node = Node::create(client, "", "startup", false);
|
||||||
|
node.add_local_signal("create_startup_settings", create_startup_settings_flex);
|
||||||
|
node.add_local_method(
|
||||||
|
"get_connection_environment",
|
||||||
|
get_connection_environment_flex,
|
||||||
|
);
|
||||||
|
node.add_to_scenegraph().map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_startup_settings_flex(
|
||||||
|
_node: &Node,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let node = Node::create(
|
||||||
|
&calling_client,
|
||||||
|
"/startup/settings",
|
||||||
|
deserialize(data)?,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.add_to_scenegraph()?;
|
||||||
|
StartupSettings::add_to(&node);
|
||||||
|
|
||||||
|
node.add_local_signal("set_root", StartupSettings::set_root_flex);
|
||||||
|
node.add_local_signal(
|
||||||
|
"add_automatic_acceptor",
|
||||||
|
StartupSettings::add_automatic_acceptor_flex,
|
||||||
|
);
|
||||||
|
node.add_local_method(
|
||||||
|
"generate_startup_token",
|
||||||
|
StartupSettings::generate_startup_token_flex,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! var_env_insert {
|
||||||
|
($env:ident, $name:ident) => {
|
||||||
|
$env.insert(stringify!($name).to_string(), $name.get().unwrap().clone());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn get_connection_environment_flex(
|
||||||
|
_node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
_data: &[u8],
|
||||||
|
) -> Result<Vec<u8>> {
|
||||||
|
let mut env: FxHashMap<String, String> = FxHashMap::default();
|
||||||
|
var_env_insert!(env, STARDUST_INSTANCE);
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
{
|
||||||
|
var_env_insert!(env, WAYLAND_DISPLAY);
|
||||||
|
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
||||||
|
env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string());
|
||||||
|
env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
|
||||||
|
env.insert("CLUTTER_BACKEND".to_string(), "wayland".to_string());
|
||||||
|
env.insert("SDL_VIDEODRIVER".to_string(), "wayland".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(serialize(env)?)
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -13,6 +13,7 @@ use serde::Serialize;
|
|||||||
use stardust_xr::schemas::{flat::Datamap, flex::flexbuffers};
|
use stardust_xr::schemas::{flat::Datamap, flex::flexbuffers};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::StereoKitMultiThread;
|
use stereokit::StereoKitMultiThread;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct KeyboardEvent {
|
pub struct KeyboardEvent {
|
||||||
@@ -35,6 +36,7 @@ impl EyePointer {
|
|||||||
|
|
||||||
Ok(EyePointer { spatial, pointer })
|
Ok(EyePointer { spatial, pointer })
|
||||||
}
|
}
|
||||||
|
#[instrument(level = "debug", name = "Update Flatscreen Pointer Ray", skip_all)]
|
||||||
pub fn update(&self, sk: &impl StereoKitMultiThread) {
|
pub fn update(&self, sk: &impl StereoKitMultiThread) {
|
||||||
let ray = sk.input_eyes();
|
let ray = sk.input_eyes();
|
||||||
self.spatial
|
self.spatial
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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::{mask_matches, Mask, PulseSender, PULSE_RECEIVER_REGISTRY},
|
||||||
fields::Ray,
|
fields::Ray,
|
||||||
input::{pointer::Pointer, InputMethod, InputType},
|
input::{pointer::Pointer, InputMethod, InputType},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
@@ -9,45 +9,28 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{vec2, vec3, Mat4, Vec2, Vec3};
|
use glam::{vec3, Mat4, Vec3};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Serialize;
|
||||||
|
use stardust_xr::schemas::{flat::Datamap, flex::flexbuffers};
|
||||||
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 tracing::instrument;
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize)]
|
const SK_KEYMAP: &str = include_str!("sk.kmp");
|
||||||
struct MouseEvent {
|
|
||||||
select: f32,
|
|
||||||
grab: f32,
|
|
||||||
scroll_continuous: Vec2,
|
|
||||||
scroll_discrete: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct KeyboardEvent {
|
pub struct KeyboardEvent {
|
||||||
pub keyboard: (),
|
pub keyboard: String,
|
||||||
pub xkbv1: (),
|
pub keymap: Option<String>,
|
||||||
pub keymap_id: String,
|
pub keys_up: Option<Vec<u32>>,
|
||||||
pub keys: Vec<i32>,
|
pub keys_down: Option<Vec<u32>>,
|
||||||
}
|
|
||||||
impl Default for KeyboardEvent {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
keyboard: (),
|
|
||||||
xkbv1: (),
|
|
||||||
keymap_id: "flatscreen".to_string(),
|
|
||||||
keys: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MousePointer {
|
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>,
|
|
||||||
keyboard_datamap: TypedDatamap<KeyboardEvent>,
|
|
||||||
keyboard_sender: Arc<PulseSender>,
|
keyboard_sender: Arc<PulseSender>,
|
||||||
}
|
}
|
||||||
impl MousePointer {
|
impl MousePointer {
|
||||||
@@ -57,26 +40,24 @@ impl MousePointer {
|
|||||||
let pointer =
|
let pointer =
|
||||||
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
|
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
|
||||||
|
|
||||||
KEYMAPS.lock().insert(
|
let keyboard_mask = {
|
||||||
"flatscreen".to_string(),
|
let mut fbb = flexbuffers::Builder::default();
|
||||||
Keymap::new_from_names(&Context::new(0), "evdev", "", "", "", None, 0)
|
let mut map = fbb.start_map();
|
||||||
.unwrap()
|
map.push("keyboard", "xkbv1");
|
||||||
.get_as_string(FORMAT_TEXT_V1),
|
map.end_map();
|
||||||
);
|
Mask(fbb.take_buffer())
|
||||||
|
};
|
||||||
let keyboard_sender =
|
let keyboard_sender = PulseSender::add_to(&node, keyboard_mask).unwrap();
|
||||||
PulseSender::add_to(&node, Mask::from_struct::<KeyboardEvent>()).unwrap();
|
|
||||||
|
|
||||||
Ok(MousePointer {
|
Ok(MousePointer {
|
||||||
node,
|
node,
|
||||||
spatial,
|
spatial,
|
||||||
pointer,
|
pointer,
|
||||||
mouse_datamap: Default::default(),
|
|
||||||
keyboard_datamap: Default::default(),
|
|
||||||
keyboard_sender,
|
keyboard_sender,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, sk: &impl StereoKitMultiThread) {
|
#[instrument(level = "debug", name = "Update Flatscreen Pointer Ray", skip_all)]
|
||||||
|
pub fn update(&self, sk: &impl StereoKitMultiThread) {
|
||||||
let mouse = sk.input_mouse();
|
let mouse = sk.input_mouse();
|
||||||
|
|
||||||
let ray = ray_from_mouse(mouse.pos).unwrap();
|
let ray = ray_from_mouse(mouse.pos).unwrap();
|
||||||
@@ -90,26 +71,35 @@ impl MousePointer {
|
|||||||
);
|
);
|
||||||
{
|
{
|
||||||
// Set pointer input datamap
|
// Set pointer input datamap
|
||||||
self.mouse_datamap.select =
|
let mut fbb = flexbuffers::Builder::default();
|
||||||
|
let mut map = fbb.start_map();
|
||||||
|
map.push(
|
||||||
|
"select",
|
||||||
if sk.input_key(Key::MouseLeft).contains(ButtonState::ACTIVE) {
|
if sk.input_key(Key::MouseLeft).contains(ButtonState::ACTIVE) {
|
||||||
1.0f32
|
1.0f32
|
||||||
} else {
|
} else {
|
||||||
0.0f32
|
0.0f32
|
||||||
};
|
},
|
||||||
self.mouse_datamap.grab = if sk.input_key(Key::MouseRight).contains(ButtonState::ACTIVE)
|
);
|
||||||
{
|
map.push(
|
||||||
1.0f32
|
"grab",
|
||||||
} else {
|
if sk.input_key(Key::MouseRight).contains(ButtonState::ACTIVE) {
|
||||||
0.0f32
|
1.0f32
|
||||||
};
|
} else {
|
||||||
self.mouse_datamap.scroll_continuous = vec2(0.0, mouse.scroll_change / 120.0);
|
0.0f32
|
||||||
self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0);
|
},
|
||||||
*self.pointer.datamap.lock() = self.mouse_datamap.to_datamap().ok();
|
);
|
||||||
|
let mut scroll_vec = map.start_vector("scroll");
|
||||||
|
scroll_vec.push(0_f32);
|
||||||
|
scroll_vec.push(mouse.scroll_change / 120.0);
|
||||||
|
scroll_vec.end_vector();
|
||||||
|
map.end_map();
|
||||||
|
*self.pointer.datamap.lock() = Datamap::new(fbb.take_buffer()).ok();
|
||||||
}
|
}
|
||||||
self.send_keyboard_input(sk);
|
self.send_keyboard_input(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_keyboard_input(&mut self, sk: &impl StereoKitMultiThread) {
|
fn send_keyboard_input(&self, sk: &impl StereoKitMultiThread) {
|
||||||
let rx = PULSE_RECEIVER_REGISTRY
|
let rx = PULSE_RECEIVER_REGISTRY
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -117,7 +107,7 @@ impl MousePointer {
|
|||||||
.map(|rx| {
|
.map(|rx| {
|
||||||
let result = rx.field.ray_march(Ray {
|
let result = rx.field.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(),
|
||||||
});
|
});
|
||||||
(rx, result)
|
(rx, result)
|
||||||
@@ -135,127 +125,29 @@ impl MousePointer {
|
|||||||
.map(|(rx, _)| rx);
|
.map(|(rx, _)| rx);
|
||||||
|
|
||||||
if let Some(rx) = rx {
|
if let Some(rx) = rx {
|
||||||
|
let mut keys_up = vec![];
|
||||||
|
let mut keys_down = vec![];
|
||||||
let keys = (8_u32..254)
|
let keys = (8_u32..254)
|
||||||
.filter_map(|i| Key::try_from(i).ok())
|
.filter_map(|i| Some((i, Key::try_from(i).ok()?)))
|
||||||
.filter_map(|k| Some((map_key(k)?, sk.input_key(k))))
|
.map(|(i, k)| (i - 8, sk.input_key(k)));
|
||||||
.filter_map(|(i, k)| {
|
for (key, state) in keys {
|
||||||
if k.contains(ButtonState::JUST_ACTIVE) {
|
if state.contains(ButtonState::JUST_ACTIVE) {
|
||||||
Some(i as i32)
|
keys_down.push(key);
|
||||||
} else if k.contains(ButtonState::JUST_INACTIVE) {
|
} else if state.contains(ButtonState::JUST_INACTIVE) {
|
||||||
Some(-(i as i32))
|
keys_up.push(key);
|
||||||
} else {
|
}
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.keyboard_datamap.keys = keys;
|
|
||||||
if !self.keyboard_datamap.keys.is_empty() {
|
|
||||||
rx.send_data(&self.node.uid, self.keyboard_datamap.serialize().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let key_event = KeyboardEvent {
|
||||||
|
keyboard: "xkbv1".to_string(),
|
||||||
|
keymap: Some(SK_KEYMAP.to_string()),
|
||||||
|
keys_up: Some(keys_up),
|
||||||
|
keys_down: Some(keys_down),
|
||||||
|
};
|
||||||
|
let mut serializer = flexbuffers::FlexbufferSerializer::new();
|
||||||
|
let _ = key_event.serialize(&mut serializer);
|
||||||
|
rx.send_data(&self.node.uid, serializer.take_buffer())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_key(key: Key) -> Option<u32> {
|
|
||||||
match key {
|
|
||||||
Key::Backspace => Some(input_event_codes::KEY_BACKSPACE!()),
|
|
||||||
Key::Tab => Some(input_event_codes::KEY_TAB!()),
|
|
||||||
Key::Return => Some(input_event_codes::KEY_ENTER!()),
|
|
||||||
Key::Shift => Some(input_event_codes::KEY_LEFTSHIFT!()),
|
|
||||||
Key::Ctrl => Some(input_event_codes::KEY_LEFTCTRL!()),
|
|
||||||
Key::Alt => Some(input_event_codes::KEY_LEFTALT!()),
|
|
||||||
Key::CapsLock => Some(input_event_codes::KEY_CAPSLOCK!()),
|
|
||||||
Key::Esc => Some(input_event_codes::KEY_ESC!()),
|
|
||||||
Key::Space => Some(input_event_codes::KEY_SPACE!()),
|
|
||||||
Key::End => Some(input_event_codes::KEY_END!()),
|
|
||||||
Key::Home => Some(input_event_codes::KEY_HOME!()),
|
|
||||||
Key::Left => Some(input_event_codes::KEY_LEFT!()),
|
|
||||||
Key::Right => Some(input_event_codes::KEY_RIGHT!()),
|
|
||||||
Key::Up => Some(input_event_codes::KEY_UP!()),
|
|
||||||
Key::Down => Some(input_event_codes::KEY_DOWN!()),
|
|
||||||
Key::PageUp => Some(input_event_codes::KEY_PAGEUP!()),
|
|
||||||
Key::PageDown => Some(input_event_codes::KEY_PAGEDOWN!()),
|
|
||||||
Key::PrintScreen => Some(input_event_codes::KEY_PRINT!()),
|
|
||||||
Key::KeyInsert => Some(input_event_codes::KEY_INSERT!()),
|
|
||||||
Key::Del => Some(input_event_codes::KEY_DELETE!()),
|
|
||||||
Key::Key0 => Some(input_event_codes::KEY_0!()),
|
|
||||||
Key::Key1 => Some(input_event_codes::KEY_1!()),
|
|
||||||
Key::Key2 => Some(input_event_codes::KEY_2!()),
|
|
||||||
Key::Key3 => Some(input_event_codes::KEY_3!()),
|
|
||||||
Key::Key4 => Some(input_event_codes::KEY_4!()),
|
|
||||||
Key::Key5 => Some(input_event_codes::KEY_5!()),
|
|
||||||
Key::Key6 => Some(input_event_codes::KEY_6!()),
|
|
||||||
Key::Key7 => Some(input_event_codes::KEY_7!()),
|
|
||||||
Key::Key8 => Some(input_event_codes::KEY_8!()),
|
|
||||||
Key::Key9 => Some(input_event_codes::KEY_9!()),
|
|
||||||
Key::A => Some(input_event_codes::KEY_A!()),
|
|
||||||
Key::B => Some(input_event_codes::KEY_B!()),
|
|
||||||
Key::C => Some(input_event_codes::KEY_C!()),
|
|
||||||
Key::D => Some(input_event_codes::KEY_D!()),
|
|
||||||
Key::E => Some(input_event_codes::KEY_E!()),
|
|
||||||
Key::F => Some(input_event_codes::KEY_F!()),
|
|
||||||
Key::G => Some(input_event_codes::KEY_G!()),
|
|
||||||
Key::H => Some(input_event_codes::KEY_H!()),
|
|
||||||
Key::I => Some(input_event_codes::KEY_I!()),
|
|
||||||
Key::J => Some(input_event_codes::KEY_J!()),
|
|
||||||
Key::K => Some(input_event_codes::KEY_K!()),
|
|
||||||
Key::L => Some(input_event_codes::KEY_L!()),
|
|
||||||
Key::M => Some(input_event_codes::KEY_M!()),
|
|
||||||
Key::N => Some(input_event_codes::KEY_N!()),
|
|
||||||
Key::O => Some(input_event_codes::KEY_O!()),
|
|
||||||
Key::P => Some(input_event_codes::KEY_P!()),
|
|
||||||
Key::Q => Some(input_event_codes::KEY_Q!()),
|
|
||||||
Key::R => Some(input_event_codes::KEY_R!()),
|
|
||||||
Key::S => Some(input_event_codes::KEY_S!()),
|
|
||||||
Key::T => Some(input_event_codes::KEY_T!()),
|
|
||||||
Key::U => Some(input_event_codes::KEY_U!()),
|
|
||||||
Key::V => Some(input_event_codes::KEY_V!()),
|
|
||||||
Key::W => Some(input_event_codes::KEY_W!()),
|
|
||||||
Key::X => Some(input_event_codes::KEY_X!()),
|
|
||||||
Key::Y => Some(input_event_codes::KEY_Y!()),
|
|
||||||
Key::Z => Some(input_event_codes::KEY_Z!()),
|
|
||||||
Key::Numpad0 => Some(input_event_codes::KEY_NUMERIC_0!()),
|
|
||||||
Key::Numpad1 => Some(input_event_codes::KEY_NUMERIC_1!()),
|
|
||||||
Key::Numpad2 => Some(input_event_codes::KEY_NUMERIC_2!()),
|
|
||||||
Key::Numpad3 => Some(input_event_codes::KEY_NUMERIC_3!()),
|
|
||||||
Key::Numpad4 => Some(input_event_codes::KEY_NUMERIC_4!()),
|
|
||||||
Key::Numpad5 => Some(input_event_codes::KEY_NUMERIC_5!()),
|
|
||||||
Key::Numpad6 => Some(input_event_codes::KEY_NUMERIC_6!()),
|
|
||||||
Key::Numpad7 => Some(input_event_codes::KEY_NUMERIC_7!()),
|
|
||||||
Key::Numpad8 => Some(input_event_codes::KEY_NUMERIC_8!()),
|
|
||||||
Key::Numpad9 => Some(input_event_codes::KEY_NUMERIC_9!()),
|
|
||||||
Key::F1 => Some(input_event_codes::KEY_F1!()),
|
|
||||||
Key::F2 => Some(input_event_codes::KEY_F2!()),
|
|
||||||
Key::F3 => Some(input_event_codes::KEY_F3!()),
|
|
||||||
Key::F4 => Some(input_event_codes::KEY_F4!()),
|
|
||||||
Key::F5 => Some(input_event_codes::KEY_F5!()),
|
|
||||||
Key::F6 => Some(input_event_codes::KEY_F6!()),
|
|
||||||
Key::F7 => Some(input_event_codes::KEY_F7!()),
|
|
||||||
Key::F8 => Some(input_event_codes::KEY_F8!()),
|
|
||||||
Key::F9 => Some(input_event_codes::KEY_F9!()),
|
|
||||||
Key::F10 => Some(input_event_codes::KEY_F10!()),
|
|
||||||
Key::F11 => Some(input_event_codes::KEY_F11!()),
|
|
||||||
Key::F12 => Some(input_event_codes::KEY_F12!()),
|
|
||||||
Key::Comma => Some(input_event_codes::KEY_COMMA!()),
|
|
||||||
Key::Period => Some(input_event_codes::KEY_DOT!()),
|
|
||||||
Key::SlashFwd => Some(input_event_codes::KEY_SLASH!()),
|
|
||||||
Key::SlashBack => Some(input_event_codes::KEY_BACKSLASH!()),
|
|
||||||
Key::Semicolon => Some(input_event_codes::KEY_SEMICOLON!()),
|
|
||||||
Key::Apostrophe => Some(input_event_codes::KEY_APOSTROPHE!()),
|
|
||||||
Key::BracketOpen => Some(input_event_codes::KEY_LEFTBRACE!()),
|
|
||||||
Key::BracketClose => Some(input_event_codes::KEY_RIGHTBRACE!()),
|
|
||||||
Key::Minus => Some(input_event_codes::KEY_MINUS!()),
|
|
||||||
Key::Equals => Some(input_event_codes::KEY_EQUAL!()),
|
|
||||||
Key::Backtick => None,
|
|
||||||
Key::LCmd => Some(input_event_codes::KEY_LEFTMETA!()),
|
|
||||||
Key::RCmd => Some(input_event_codes::KEY_RIGHTMETA!()),
|
|
||||||
Key::Multiply => Some(input_event_codes::KEY_NUMERIC_STAR!()),
|
|
||||||
Key::Add => Some(input_event_codes::KEY_KPPLUS!()),
|
|
||||||
Key::Subtract => Some(input_event_codes::KEY_MINUS!()),
|
|
||||||
Key::Decimal => Some(input_event_codes::KEY_DOT!()),
|
|
||||||
Key::Divide => Some(input_event_codes::KEY_SLASH!()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
275
src/objects/input/sk.kmp
Normal file
275
src/objects/input/sk.kmp
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
xkb_keymap {
|
||||||
|
|
||||||
|
default xkb_keycodes "basic" {
|
||||||
|
minimum = 8;
|
||||||
|
maximum = 255;
|
||||||
|
|
||||||
|
<backspace> = 8;
|
||||||
|
<tab> = 9;
|
||||||
|
<return> = 13;
|
||||||
|
<shift> = 16;
|
||||||
|
<ctrl> = 17;
|
||||||
|
<alt> = 18;
|
||||||
|
<caps_lock> = 20;
|
||||||
|
<esc> = 27;
|
||||||
|
<space> = 32;
|
||||||
|
<end> = 35;
|
||||||
|
<home> = 36;
|
||||||
|
<left> = 37;
|
||||||
|
<right> = 39;
|
||||||
|
<up> = 38;
|
||||||
|
<down> = 40;
|
||||||
|
<page_up> = 33;
|
||||||
|
<page_down> = 34;
|
||||||
|
<printscreen> = 42;
|
||||||
|
<key_insert> = 45;
|
||||||
|
<del> = 46;
|
||||||
|
|
||||||
|
<0> = 48;
|
||||||
|
<1> = 49;
|
||||||
|
<2> = 50;
|
||||||
|
<3> = 51;
|
||||||
|
<4> = 52;
|
||||||
|
<5> = 53;
|
||||||
|
<6> = 54;
|
||||||
|
<7> = 55;
|
||||||
|
<8> = 56;
|
||||||
|
<9> = 57;
|
||||||
|
|
||||||
|
<a> = 65;
|
||||||
|
<b> = 66;
|
||||||
|
<c> = 67;
|
||||||
|
<d> = 68;
|
||||||
|
<e> = 69;
|
||||||
|
<f> = 70;
|
||||||
|
<g> = 71;
|
||||||
|
<h> = 72;
|
||||||
|
<i> = 73;
|
||||||
|
<j> = 74;
|
||||||
|
<k> = 75;
|
||||||
|
<l> = 76;
|
||||||
|
<m> = 77;
|
||||||
|
<n> = 78;
|
||||||
|
<o> = 79;
|
||||||
|
<p> = 80;
|
||||||
|
<q> = 81;
|
||||||
|
<r> = 82;
|
||||||
|
<s> = 83;
|
||||||
|
<t> = 84;
|
||||||
|
<u> = 85;
|
||||||
|
<v> = 86;
|
||||||
|
<w> = 87;
|
||||||
|
<x> = 88;
|
||||||
|
<y> = 89;
|
||||||
|
<z> = 90;
|
||||||
|
|
||||||
|
<num0> = 96;
|
||||||
|
<num1> = 97;
|
||||||
|
<num2> = 98;
|
||||||
|
<num3> = 99;
|
||||||
|
<num4> = 100;
|
||||||
|
<num5> = 101;
|
||||||
|
<num6> = 102;
|
||||||
|
<num7> = 103;
|
||||||
|
<num8> = 104;
|
||||||
|
<num9> = 105;
|
||||||
|
|
||||||
|
<f1> = 112;
|
||||||
|
<f2> = 113;
|
||||||
|
<f3> = 114;
|
||||||
|
<f4> = 115;
|
||||||
|
<f5> = 116;
|
||||||
|
<f6> = 117;
|
||||||
|
<f7> = 118;
|
||||||
|
<f8> = 119;
|
||||||
|
<f9> = 120;
|
||||||
|
<f10> = 121;
|
||||||
|
<f11> = 122;
|
||||||
|
<f12> = 123;
|
||||||
|
|
||||||
|
<comma> = 188;
|
||||||
|
<period> = 190;
|
||||||
|
<slash_fwd> = 191;
|
||||||
|
<slash_back> = 220;
|
||||||
|
<semicolon> = 186;
|
||||||
|
<apostrophe> = 222;
|
||||||
|
<bracket_open> = 219;
|
||||||
|
<bracket_close> = 221;
|
||||||
|
<minus> = 189;
|
||||||
|
<equals> = 187;
|
||||||
|
<backtick> = 192;
|
||||||
|
<lcmd> = 91;
|
||||||
|
<rcmd> = 92;
|
||||||
|
<multiply> = 106;
|
||||||
|
<add> = 107;
|
||||||
|
<subtract> = 109;
|
||||||
|
<decimal> = 110;
|
||||||
|
<divide> = 111;
|
||||||
|
};
|
||||||
|
|
||||||
|
partial default xkb_types "basic" {
|
||||||
|
virtual_modifiers Alt;
|
||||||
|
|
||||||
|
type "ONE_LEVEL" {
|
||||||
|
modifiers= none;
|
||||||
|
level_name[1]= "Any";
|
||||||
|
};
|
||||||
|
type "TWO_LEVEL" {
|
||||||
|
modifiers= Shift;
|
||||||
|
map[Shift]= 2;
|
||||||
|
level_name[1]= "Base";
|
||||||
|
level_name[2]= "Shift";
|
||||||
|
};
|
||||||
|
type "ALPHABETIC" {
|
||||||
|
modifiers= Shift+Lock;
|
||||||
|
map[Shift]= 2;
|
||||||
|
map[Lock]= 2;
|
||||||
|
level_name[1]= "Base";
|
||||||
|
level_name[2]= "Caps";
|
||||||
|
};
|
||||||
|
type "SHIFT+ALT" {
|
||||||
|
modifiers= Shift+Alt;
|
||||||
|
map[Shift+Alt]= 2;
|
||||||
|
level_name[1]= "Base";
|
||||||
|
level_name[2]= "Shift+Alt";
|
||||||
|
};
|
||||||
|
type "PC_CONTROL_LEVEL2" {
|
||||||
|
modifiers= Control;
|
||||||
|
map[Control]= 2;
|
||||||
|
level_name[1]= "Base";
|
||||||
|
level_name[2]= "Control";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
partial default xkb_compatibility "basic" {
|
||||||
|
interpret.useModMapMods= AnyLevel;
|
||||||
|
interpret.repeat= False;
|
||||||
|
|
||||||
|
interpret ISO_Level2_Latch+Exactly(Shift) {
|
||||||
|
useModMapMods=level1;
|
||||||
|
action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
|
||||||
|
};
|
||||||
|
interpret Caps_Lock+AnyOfOrNone(all) {
|
||||||
|
action= LockMods(modifiers=Lock);
|
||||||
|
};
|
||||||
|
indicator "Caps Lock" {
|
||||||
|
whichModState= locked;
|
||||||
|
modifiers= Lock;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
default xkb_symbols "basic" {
|
||||||
|
name[Group1]="English (US)";
|
||||||
|
key <backspace> { [ BackSpace, BackSpace ] };
|
||||||
|
key <tab> { [ Tab, ISO_Left_Tab ] };
|
||||||
|
key <return> { [ Return ] };
|
||||||
|
key <shift> { [ Shift_L ] };
|
||||||
|
key <shift> { [ Shift_R ] };
|
||||||
|
key <ctrl> { [ Control_L ] };
|
||||||
|
key <ctrl> { [ Control_R ] };
|
||||||
|
key <alt> { [ Alt_L ] };
|
||||||
|
key <alt> { [ Alt_R ] };
|
||||||
|
key <caps_lock> { [ Caps_Lock ] };
|
||||||
|
key <esc> { [ Escape ] };
|
||||||
|
key <space> { [ space ] };
|
||||||
|
key <end> { [ End ] };
|
||||||
|
key <home> { [ Home ] };
|
||||||
|
key <left> { [ Left ] };
|
||||||
|
key <right> { [ Right ] };
|
||||||
|
key <up> { [ Up ] };
|
||||||
|
key <down> { [ Down ] };
|
||||||
|
key <page_up> { [ Page_Up ] };
|
||||||
|
key <page_down> { [ Page_Down ] };
|
||||||
|
key <printscreen> { [ Print ] };
|
||||||
|
key <key_insert> { [ Insert ] };
|
||||||
|
key <del> { [ Delete ] };
|
||||||
|
|
||||||
|
key <1> { [ 1, exclam ] };
|
||||||
|
key <2> { [ 2, at ] };
|
||||||
|
key <3> { [ 3, numbersign ] };
|
||||||
|
key <4> { [ 4, dollar ] };
|
||||||
|
key <5> { [ 5, percent ] };
|
||||||
|
key <6> { [ 6, asciicircum ] };
|
||||||
|
key <7> { [ 7, ampersand ] };
|
||||||
|
key <8> { [ 8, asterisk ] };
|
||||||
|
key <9> { [ 9, parenleft ] };
|
||||||
|
key <0> { [ 0, parenright ] };
|
||||||
|
|
||||||
|
key <a> { [ a, A ] };
|
||||||
|
key <b> { [ b, B ] };
|
||||||
|
key <c> { [ c, C ] };
|
||||||
|
key <d> { [ d, D ] };
|
||||||
|
key <e> { [ e, E ] };
|
||||||
|
key <f> { [ f, F ] };
|
||||||
|
key <g> { [ g, G ] };
|
||||||
|
key <h> { [ h, H ] };
|
||||||
|
key <i> { [ i, I ] };
|
||||||
|
key <j> { [ j, J ] };
|
||||||
|
key <k> { [ k, K ] };
|
||||||
|
key <l> { [ l, L ] };
|
||||||
|
key <m> { [ m, M ] };
|
||||||
|
key <n> { [ n, N ] };
|
||||||
|
key <o> { [ o, O ] };
|
||||||
|
key <p> { [ p, P ] };
|
||||||
|
key <q> { [ q, Q ] };
|
||||||
|
key <r> { [ r, R ] };
|
||||||
|
key <s> { [ s, S ] };
|
||||||
|
key <t> { [ t, T ] };
|
||||||
|
key <u> { [ u, U ] };
|
||||||
|
key <v> { [ v, V ] };
|
||||||
|
key <w> { [ w, W ] };
|
||||||
|
key <x> { [ x, X ] };
|
||||||
|
key <y> { [ y, Y ] };
|
||||||
|
key <z> { [ z, Z ] };
|
||||||
|
|
||||||
|
key <num0> { [ KP_0 ] };
|
||||||
|
key <num1> { [ KP_1 ] };
|
||||||
|
key <num2> { [ KP_2 ] };
|
||||||
|
key <num3> { [ KP_3 ] };
|
||||||
|
key <num4> { [ KP_4 ] };
|
||||||
|
key <num5> { [ KP_5 ] };
|
||||||
|
key <num6> { [ KP_6 ] };
|
||||||
|
key <num7> { [ KP_7 ] };
|
||||||
|
key <num8> { [ KP_8 ] };
|
||||||
|
key <num9> { [ KP_9 ] };
|
||||||
|
|
||||||
|
key <f1> { [ F1 ] };
|
||||||
|
key <f2> { [ F2 ] };
|
||||||
|
key <f3> { [ F3 ] };
|
||||||
|
key <f4> { [ F4 ] };
|
||||||
|
key <f5> { [ F5 ] };
|
||||||
|
key <f6> { [ F6 ] };
|
||||||
|
key <f7> { [ F7 ] };
|
||||||
|
key <f8> { [ F8 ] };
|
||||||
|
key <f9> { [ F9 ] };
|
||||||
|
key <f10> { [ F10 ] };
|
||||||
|
key <f11> { [ F11 ] };
|
||||||
|
key <f12> { [ F12 ] };
|
||||||
|
|
||||||
|
key <comma> { [ comma, less ] };
|
||||||
|
key <period> { [ period, greater ] };
|
||||||
|
key <slash_fwd> { [ slash, question ] };
|
||||||
|
key <slash_back> { [ backslash, bar ] };
|
||||||
|
key <semicolon> { [ semicolon, colon ] };
|
||||||
|
key <apostrophe> { [ apostrophe ] };
|
||||||
|
key <bracket_open> { [ bracketleft, braceleft ] };
|
||||||
|
key <bracket_close> { [ bracketright, braceright ] };
|
||||||
|
key <minus> { [ minus, underscore ] };
|
||||||
|
key <equals> { [ equal, plus ] };
|
||||||
|
key <backtick> { [ grave, asciitilde ] };
|
||||||
|
key <lcmd> { [ Super_L ] };
|
||||||
|
key <rcmd> { [ Super_R ] };
|
||||||
|
key <multiply> { [ KP_Multiply ] };
|
||||||
|
key <add> { [ KP_Add ] };
|
||||||
|
key <subtract> { [ KP_Subtract ] };
|
||||||
|
key <decimal> { [ KP_Decimal ] };
|
||||||
|
key <divide> { [ KP_Divide ] };
|
||||||
|
|
||||||
|
modifier_map Shift { <shift> };
|
||||||
|
modifier_map Lock { <caps_lock> };
|
||||||
|
modifier_map Control { <caps_lock> };
|
||||||
|
modifier_map Mod1 { <alt> };
|
||||||
|
modifier_map Mod4 { <lcmd>, <rcmd> };
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
@@ -7,72 +7,51 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::Mat4;
|
||||||
use serde::{Deserialize, Serialize};
|
use nanoid::nanoid;
|
||||||
use std::sync::Arc;
|
use stardust_xr::{
|
||||||
use stereokit::{
|
schemas::{flat::Datamap, flex::flexbuffers},
|
||||||
named_colors::WHITE, ButtonState, Handed, Model, RenderLayer, StereoKitDraw,
|
values::Transform,
|
||||||
StereoKitMultiThread,
|
|
||||||
};
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
#[derive(Default, Deserialize, Serialize)]
|
use stereokit::{ButtonState, Handed, StereoKitMultiThread};
|
||||||
struct ControllerDatamap {
|
use tracing::instrument;
|
||||||
select: f32,
|
|
||||||
grab: f32,
|
|
||||||
scroll: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SkController {
|
pub struct SkController {
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
model: Model,
|
|
||||||
handed: Handed,
|
handed: Handed,
|
||||||
datamap: TypedDatamap<ControllerDatamap>,
|
|
||||||
}
|
}
|
||||||
impl SkController {
|
impl SkController {
|
||||||
pub fn new(sk: &impl StereoKitMultiThread, handed: Handed) -> Result<Self> {
|
pub fn new(handed: Handed) -> Result<Self> {
|
||||||
let _node = Node::create(
|
let _node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
||||||
&INTERNAL_CLIENT,
|
|
||||||
"",
|
|
||||||
if handed == Handed::Left {
|
|
||||||
"controller_left"
|
|
||||||
} else {
|
|
||||||
"controller_right"
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.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 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)?;
|
||||||
Ok(SkController {
|
Ok(SkController {
|
||||||
_node,
|
_node,
|
||||||
input,
|
input,
|
||||||
handed,
|
handed,
|
||||||
model,
|
|
||||||
datamap: Default::default(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, sk: &impl StereoKitDraw) {
|
#[instrument(level = "debug", name = "Update StereoKit Tip Input Method", skip_all)]
|
||||||
|
pub fn update(&mut self, sk: &impl StereoKitMultiThread) {
|
||||||
let controller = sk.input_controller(self.handed);
|
let controller = sk.input_controller(self.handed);
|
||||||
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::ACTIVE);
|
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::ACTIVE);
|
||||||
if *self.input.enabled.lock() {
|
if *self.input.enabled.lock() {
|
||||||
let world_transform = Mat4::from_rotation_translation(
|
self.input.spatial.set_local_transform_components(
|
||||||
controller.aim.orientation,
|
None,
|
||||||
controller.aim.position,
|
Transform::from_position_rotation(
|
||||||
|
controller.pose.position,
|
||||||
|
controller.pose.orientation,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
sk.model_draw(
|
|
||||||
&self.model,
|
|
||||||
world_transform * Mat4::from_scale(Vec3::ONE * 0.02),
|
|
||||||
WHITE,
|
|
||||||
RenderLayer::LAYER0,
|
|
||||||
);
|
|
||||||
self.input.spatial.set_local_transform(world_transform);
|
|
||||||
}
|
}
|
||||||
self.datamap.select = controller.trigger;
|
let mut fbb = flexbuffers::Builder::default();
|
||||||
self.datamap.grab = controller.grip;
|
let mut map = fbb.start_map();
|
||||||
self.datamap.scroll = controller.stick;
|
map.push("select", controller.trigger);
|
||||||
*self.input.datamap.lock() = self.datamap.to_datamap().ok();
|
map.push("grab", controller.grip);
|
||||||
|
map.end_map();
|
||||||
|
*self.input.datamap.lock() = Datamap::new(fbb.take_buffer()).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,
|
||||||
@@ -9,31 +9,26 @@ use crate::{
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use stardust_xr::schemas::{
|
||||||
use stardust_xr::schemas::flat::{Hand as FlatHand, Joint};
|
flat::{Datamap, Hand as FlatHand, Joint},
|
||||||
|
flex::flexbuffers,
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
|
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
fn convert_joint(joint: HandJoint) -> Joint {
|
fn convert_joint(joint: HandJoint) -> Joint {
|
||||||
Joint {
|
Joint {
|
||||||
position: joint.position.into(),
|
position: joint.position.into(),
|
||||||
rotation: joint.orientation.into(),
|
rotation: joint.orientation.into(),
|
||||||
radius: joint.radius,
|
radius: joint.radius,
|
||||||
distance: 0.0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize)]
|
|
||||||
struct HandDatamap {
|
|
||||||
pinch_strength: f32,
|
|
||||||
grab_strength: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SkHand {
|
pub struct SkHand {
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
handed: Handed,
|
handed: Handed,
|
||||||
datamap: TypedDatamap<HandDatamap>,
|
|
||||||
}
|
}
|
||||||
impl SkHand {
|
impl SkHand {
|
||||||
pub fn new(handed: Handed) -> Result<Self> {
|
pub fn new(handed: Handed) -> Result<Self> {
|
||||||
@@ -50,20 +45,15 @@ impl SkHand {
|
|||||||
_node,
|
_node,
|
||||||
input,
|
input,
|
||||||
handed,
|
handed,
|
||||||
datamap: Default::default(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, controller_enabled: bool, sk: &impl StereoKitMultiThread) {
|
#[instrument(level = "debug", name = "Update Hand Input Method", skip_all)]
|
||||||
|
pub fn update(&mut self, sk: &impl StereoKitMultiThread) {
|
||||||
let sk_hand = sk.input_hand(self.handed);
|
let sk_hand = sk.input_hand(self.handed);
|
||||||
if let InputType::Hand(hand) = &mut *self.input.specialization.lock() {
|
if let InputType::Hand(hand) = &mut *self.input.specialization.lock() {
|
||||||
let controller_active = controller_enabled
|
let controller = sk.input_controller(self.handed);
|
||||||
&& sk
|
*self.input.enabled.lock() = controller.tracked.contains(ButtonState::INACTIVE)
|
||||||
.input_controller(self.handed)
|
&& sk_hand.tracked_state.contains(ButtonState::ACTIVE);
|
||||||
.tracked
|
|
||||||
.contains(ButtonState::ACTIVE);
|
|
||||||
*self.input.enabled.lock() =
|
|
||||||
!controller_active && sk_hand.tracked_state.contains(ButtonState::ACTIVE);
|
|
||||||
sk.input_hand_visible(self.handed, *self.input.enabled.lock());
|
|
||||||
if *self.input.enabled.lock() {
|
if *self.input.enabled.lock() {
|
||||||
hand.base.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
|
hand.base.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
|
||||||
hand.base.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
|
hand.base.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
|
||||||
@@ -96,8 +86,11 @@ impl SkHand {
|
|||||||
hand.base.elbow = None;
|
hand.base.elbow = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
let mut fbb = flexbuffers::Builder::default();
|
||||||
self.datamap.grab_strength = sk_hand.grip_activation;
|
let mut map = fbb.start_map();
|
||||||
*self.input.datamap.lock() = self.datamap.to_datamap().ok();
|
map.push("grab_strength", sk_hand.grip_activation);
|
||||||
|
map.push("pinch_strength", sk_hand.pinch_activation);
|
||||||
|
map.end_map();
|
||||||
|
*self.input.datamap.lock() = Datamap::new(fbb.take_buffer()).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod play_space;
|
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use glam::Mat4;
|
|
||||||
use mint::Vector2;
|
|
||||||
use nanoid::nanoid;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use stereokit::StereoKitMultiThread;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
core::client::INTERNAL_CLIENT,
|
|
||||||
nodes::{
|
|
||||||
data::{Mask, PulseReceiver},
|
|
||||||
fields::{r#box::BoxField, Field},
|
|
||||||
spatial::Spatial,
|
|
||||||
Node,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
struct PlaySpaceMap {
|
|
||||||
play_space: (),
|
|
||||||
size: Vector2<f32>,
|
|
||||||
}
|
|
||||||
impl Default for PlaySpaceMap {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
play_space: (),
|
|
||||||
size: [0.0; 2].into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PlaySpace {
|
|
||||||
_node: Arc<Node>,
|
|
||||||
spatial: Arc<Spatial>,
|
|
||||||
field: Arc<Field>,
|
|
||||||
_pulse_rx: Arc<PulseReceiver>,
|
|
||||||
}
|
|
||||||
impl PlaySpace {
|
|
||||||
pub fn new() -> Result<Self> {
|
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false)?;
|
|
||||||
let field = BoxField::add_to(&node, [0.0; 3].into())?;
|
|
||||||
|
|
||||||
let pulse_rx =
|
|
||||||
PulseReceiver::add_to(&node, field.clone(), Mask::from_struct::<PlaySpaceMap>())?;
|
|
||||||
|
|
||||||
Ok(PlaySpace {
|
|
||||||
_node: node,
|
|
||||||
spatial,
|
|
||||||
field,
|
|
||||||
_pulse_rx: pulse_rx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn update(&self, sk: &impl StereoKitMultiThread) {
|
|
||||||
let pose = sk.world_get_bounds_pose();
|
|
||||||
self.spatial
|
|
||||||
.set_local_transform(Mat4::from_rotation_translation(
|
|
||||||
pose.orientation,
|
|
||||||
pose.position,
|
|
||||||
));
|
|
||||||
let Field::Box(box_field) = self.field.as_ref() else {return};
|
|
||||||
box_field.set_size(
|
|
||||||
[
|
|
||||||
sk.world_get_bounds_size().x,
|
|
||||||
0.0,
|
|
||||||
sk.world_get_bounds_size().y,
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,6 @@ use crate::wayland::surface::CoreSurface;
|
|||||||
|
|
||||||
use super::state::{ClientState, WaylandState};
|
use super::state::{ClientState, WaylandState};
|
||||||
use portable_atomic::{AtomicU32, Ordering};
|
use portable_atomic::{AtomicU32, Ordering};
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
use smithay::xwayland::XWaylandClientData;
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
delegate_compositor,
|
delegate_compositor,
|
||||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
||||||
@@ -25,9 +23,11 @@ impl CompositorHandler for WaylandState {
|
|||||||
.data_map
|
.data_map
|
||||||
.insert_if_missing_threadsafe(|| AtomicU32::new(0));
|
.insert_if_missing_threadsafe(|| AtomicU32::new(0));
|
||||||
if !count_new {
|
if !count_new {
|
||||||
if let Some(stored_count) = data.data_map.get::<AtomicU32>() {
|
count = data
|
||||||
count = stored_count.fetch_add(1, Ordering::Relaxed);
|
.data_map
|
||||||
}
|
.get::<AtomicU32>()
|
||||||
|
.unwrap()
|
||||||
|
.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
||||||
@@ -38,15 +38,7 @@ impl CompositorHandler for WaylandState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
||||||
if let Some(client_state) = client.get_data::<ClientState>() {
|
&client.get_data::<ClientState>().unwrap().compositor_state
|
||||||
&client_state.compositor_state
|
|
||||||
} else {
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
if let Some(xwayland_client_data) = client.get_data::<XWaylandClientData>() {
|
|
||||||
return &xwayland_client_data.compositor_state;
|
|
||||||
}
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,10 +88,9 @@ impl KdeDecorationHandler for WaylandState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
_surface: &WlSurface,
|
_surface: &WlSurface,
|
||||||
decoration: &OrgKdeKwinServerDecoration,
|
decoration: &OrgKdeKwinServerDecoration,
|
||||||
mode: WEnum<KdeMode>,
|
_mode: WEnum<KdeMode>,
|
||||||
) {
|
) {
|
||||||
let Ok(mode) = mode.into_result() else {return};
|
decoration.mode(KdeMode::Server);
|
||||||
decoration.mode(mode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate_kde_decoration!(WaylandState);
|
delegate_kde_decoration!(WaylandState);
|
||||||
|
|||||||
@@ -1,140 +1,144 @@
|
|||||||
mod compositor;
|
mod compositor;
|
||||||
mod data_device;
|
mod data_device;
|
||||||
mod decoration;
|
mod decoration;
|
||||||
|
pub mod panel_item;
|
||||||
mod seat;
|
mod seat;
|
||||||
|
mod shaders;
|
||||||
mod state;
|
mod state;
|
||||||
mod surface;
|
mod surface;
|
||||||
// mod xdg_activation;
|
// mod xdg_activation;
|
||||||
mod xdg_shell;
|
mod xdg_shell;
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
pub mod xwayland;
|
|
||||||
|
|
||||||
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::{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::egl::EGLContext;
|
||||||
use smithay::backend::renderer::ImportDma;
|
use smithay::backend::renderer::gles::GlesRenderer;
|
||||||
use smithay::reexports::wayland_server::backend::ClientId;
|
use smithay::reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket};
|
||||||
use smithay::reexports::wayland_server::DisplayHandle;
|
|
||||||
use smithay::reexports::wayland_server::{Display, ListeningSocket};
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
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,
|
||||||
};
|
};
|
||||||
use stereokit as sk;
|
use stereokit as sk;
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle,
|
io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle,
|
||||||
};
|
};
|
||||||
use tracing::{debug_span, info, instrument};
|
use tracing::{debug, debug_span, info, instrument};
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
pub struct DisplayWrapper(Mutex<Display<WaylandState>>, DisplayHandle);
|
struct EGLRawHandles {
|
||||||
impl DisplayWrapper {
|
display: *const c_void,
|
||||||
pub fn handle(&self) -> DisplayHandle {
|
config: *const c_void,
|
||||||
self.1.clone()
|
context: *const c_void,
|
||||||
}
|
}
|
||||||
pub fn dispatch_clients(&self, state: &mut WaylandState) -> Result<usize, std::io::Error> {
|
fn get_sk_egl() -> Result<EGLRawHandles> {
|
||||||
self.0.lock().dispatch_clients(state)
|
ensure!(
|
||||||
}
|
unsafe { sk::sys::backend_graphics_get() }
|
||||||
pub fn flush_clients(&self, client: Option<ClientId>) {
|
== sk::sys::backend_graphics__backend_graphics_opengles_egl,
|
||||||
if let Some(mut lock) = self.0.try_lock() {
|
"StereoKit is not running using EGL!"
|
||||||
let _ = lock.backend().flush(client);
|
);
|
||||||
|
|
||||||
|
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 fn poll_fd(&self) -> Result<OwnedFd, std::io::Error> {
|
|
||||||
self.0.lock().backend().poll_fd().try_clone_to_owned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GLOBAL_DESTROY_QUEUE: OnceCell<mpsc::Sender<GlobalId>> = OnceCell::new();
|
||||||
|
|
||||||
pub struct Wayland {
|
pub struct Wayland {
|
||||||
display: Arc<DisplayWrapper>,
|
display: Arc<Mutex<Display<WaylandState>>>,
|
||||||
pub socket_name: Option<String>,
|
pub socket_name: String,
|
||||||
join_handle: JoinHandle<Result<()>>,
|
join_handle: JoinHandle<Result<()>>,
|
||||||
dmabuf_rx: UnboundedReceiver<Dmabuf>,
|
renderer: GlesRenderer,
|
||||||
wayland_state: Arc<Mutex<WaylandState>>,
|
state: Arc<Mutex<WaylandState>>,
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
pub xwayland_state: xwayland::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 display = Arc::new(Mutex::new(display));
|
||||||
let display = Arc::new(DisplayWrapper(Mutex::new(display), display_handle.clone()));
|
let state = WaylandState::new(display.clone(), display_handle, &renderer);
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
let xwayland_state = xwayland::XWaylandState::create(&display_handle)?;
|
let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8);
|
||||||
let wayland_state = WaylandState::new(display_handle, &buffer_manager.renderer, dmabuf_tx);
|
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
|
||||||
|
|
||||||
let socket = ListeningSocket::bind_auto("wayland", 0..33)?;
|
let socket = ListeningSocket::bind_auto("wayland", 0..33)?;
|
||||||
let socket_name = socket
|
let socket_name = socket.socket_name().unwrap().to_str().unwrap().to_string();
|
||||||
.socket_name()
|
WAYLAND_DISPLAY
|
||||||
.and_then(OsStr::to_str)
|
.set(socket_name.clone())
|
||||||
.map(ToString::to_string);
|
.expect("seriously message nova this time they screwed up big time");
|
||||||
if let Some(socket_name) = &socket_name {
|
|
||||||
let _ = WAYLAND_DISPLAY.set(socket_name.clone());
|
|
||||||
}
|
|
||||||
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, state.clone(), global_destroy_queue)?;
|
||||||
|
|
||||||
Ok(Wayland {
|
Ok(Wayland {
|
||||||
display,
|
display,
|
||||||
socket_name,
|
socket_name,
|
||||||
join_handle,
|
join_handle,
|
||||||
dmabuf_rx,
|
renderer,
|
||||||
wayland_state,
|
state,
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
xwayland_state,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_loop(
|
fn start_loop(
|
||||||
display: Arc<DisplayWrapper>,
|
display: Arc<Mutex<Display<WaylandState>>>,
|
||||||
socket: ListeningSocket,
|
socket: ListeningSocket,
|
||||||
state: Arc<Mutex<WaylandState>>,
|
state: Arc<Mutex<WaylandState>>,
|
||||||
|
mut global_destroy_queue: mpsc::Receiver<GlobalId>,
|
||||||
) -> Result<JoinHandle<Result<()>>> {
|
) -> Result<JoinHandle<Result<()>>> {
|
||||||
let listen_async =
|
let listen_async =
|
||||||
AsyncUnixListener::from_std(unsafe { UnixListener::from_raw_fd(socket.as_raw_fd()) })?;
|
AsyncUnixListener::from_std(unsafe { UnixListener::from_raw_fd(socket.as_raw_fd()) })?;
|
||||||
|
|
||||||
let dispatch_poll_fd = display.poll_fd()?;
|
let dispatch_poll_fd = display.lock().backend().poll_fd().try_clone_to_owned()?;
|
||||||
let dispatch_poll_listener = AsyncFd::new(dispatch_poll_fd)?;
|
let dispatch_poll_listener = AsyncFd::new(dispatch_poll_fd)?;
|
||||||
|
|
||||||
let dh1 = display.handle();
|
let dh1 = display.lock().handle();
|
||||||
let mut dh2 = dh1.clone();
|
let mut dh2 = dh1.clone();
|
||||||
|
|
||||||
Ok(task::new(|| "wayland loop", async move {
|
Ok(task::new(|| "wayland loop", async move {
|
||||||
let _socket = socket; // Keep the socket alive
|
let _socket = socket; // Keep the socket alive
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
e = global_destroy_queue.recv() => { // New global to destroy
|
||||||
|
debug!(?e, "destroy global");
|
||||||
|
dh1.remove_global::<WaylandState>(e.unwrap());
|
||||||
|
}
|
||||||
acc = listen_async.accept() => { // New client connected
|
acc = listen_async.accept() => { // New client connected
|
||||||
let (stream, _) = acc?;
|
let (stream, _) = acc?;
|
||||||
let client_state = Arc::new(ClientState {
|
let client = dh2.insert_client(stream.into_std()?, Arc::new(ClientState::default()))?;
|
||||||
id: OnceCell::new(),
|
|
||||||
compositor_state: Default::default(),
|
state.lock().new_client(client.id(), &dh2);
|
||||||
display: Arc::downgrade(&display),
|
|
||||||
seat: SeatData::new(&dh1)
|
|
||||||
});
|
|
||||||
let client = dh2.insert_client(stream.into_std()?, client_state.clone())?;
|
|
||||||
let _ = client_state.seat.client.set(client.id());
|
|
||||||
}
|
}
|
||||||
e = dispatch_poll_listener.readable() => { // Dispatch
|
e = dispatch_poll_listener.readable() => { // Dispatch
|
||||||
let mut guard = e?;
|
let mut guard = e?;
|
||||||
debug_span!("Dispatch wayland event").in_scope(|| -> Result<(), color_eyre::Report> {
|
debug_span!("Dispatch wayland event").in_scope(|| -> Result<(), color_eyre::Report> {
|
||||||
|
let mut display = display.lock();
|
||||||
display.dispatch_clients(&mut *state.lock())?;
|
display.dispatch_clients(&mut *state.lock())?;
|
||||||
display.flush_clients(None);
|
display.flush_clients()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
guard.clear_ready();
|
guard.clear_ready();
|
||||||
@@ -144,27 +148,26 @@ 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",
|
|
||||||
skip(self, sk, buffer_manager)
|
|
||||||
)]
|
|
||||||
pub fn update(&mut self, sk: &impl StereoKitDraw, buffer_manager: &mut BufferManager) {
|
|
||||||
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.lock().flush_clients().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_event(&self, sk: &impl StereoKitDraw) {
|
pub fn frame_event(&self, sk: &impl StereoKitDraw) {
|
||||||
let output = self.wayland_state.lock().output.clone();
|
let state = self.state.lock();
|
||||||
|
|
||||||
for core_surface in CORE_SURFACES.get_valid_contents() {
|
for core_surface in CORE_SURFACES.get_valid_contents() {
|
||||||
core_surface.frame(sk, output.clone());
|
core_surface.frame(sk, state.output.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_context_current(&self) {
|
||||||
|
unsafe {
|
||||||
|
self.renderer.egl_context().make_current().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
655
src/wayland/panel_item.rs
Normal file
655
src/wayland/panel_item.rs
Normal file
@@ -0,0 +1,655 @@
|
|||||||
|
use super::{
|
||||||
|
seat::{Cursor, SeatData},
|
||||||
|
surface::CoreSurface,
|
||||||
|
xdg_shell::{PopupData, ToplevelData, XdgSurfaceData},
|
||||||
|
SERIAL_COUNTER,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
core::{
|
||||||
|
client::{get_env, startup_settings, Client, INTERNAL_CLIENT},
|
||||||
|
registry::Registry,
|
||||||
|
},
|
||||||
|
nodes::{
|
||||||
|
drawable::Drawable,
|
||||||
|
items::{self, Item, ItemSpecialization, ItemType, TypeInfo},
|
||||||
|
spatial::Spatial,
|
||||||
|
Node,
|
||||||
|
},
|
||||||
|
wayland::seat::{KeyboardEvent, PointerEvent},
|
||||||
|
};
|
||||||
|
use color_eyre::eyre::{bail, eyre, Result};
|
||||||
|
use glam::Mat4;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use mint::Vector2;
|
||||||
|
use nanoid::nanoid;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use serde::{
|
||||||
|
de::{Deserializer, Error, SeqAccess, Visitor},
|
||||||
|
ser::Serializer,
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
use smithay::{
|
||||||
|
reexports::{
|
||||||
|
wayland_protocols::xdg::shell::server::{
|
||||||
|
xdg_popup::XdgPopup,
|
||||||
|
xdg_surface::XdgSurface,
|
||||||
|
xdg_toplevel::{XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE},
|
||||||
|
},
|
||||||
|
wayland_server::{
|
||||||
|
backend::Credentials, protocol::wl_surface::WlSurface, Resource, Weak as WlWeak,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wayland::compositor,
|
||||||
|
};
|
||||||
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use tracing::debug;
|
||||||
|
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
||||||
|
type_name: "panel",
|
||||||
|
aliased_local_signals: vec![
|
||||||
|
"apply_surface_material",
|
||||||
|
"configure_toplevel",
|
||||||
|
"set_toplevel_capabilities",
|
||||||
|
"pointer_scroll",
|
||||||
|
"pointer_button",
|
||||||
|
"pointer_motion",
|
||||||
|
"keyboard_key",
|
||||||
|
"keyboard_set_keymap_names",
|
||||||
|
"keyboard_set_keymap_string",
|
||||||
|
"close",
|
||||||
|
],
|
||||||
|
aliased_local_methods: vec![],
|
||||||
|
aliased_remote_signals: vec![
|
||||||
|
"commit_toplevel",
|
||||||
|
"recommend_toplevel_state",
|
||||||
|
"set_cursor",
|
||||||
|
"new_popup",
|
||||||
|
"reposition_popup",
|
||||||
|
"drop_popup",
|
||||||
|
],
|
||||||
|
ui: Default::default(),
|
||||||
|
items: Registry::new(),
|
||||||
|
acceptors: Registry::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ID for a surface inside this panel item
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum SurfaceID {
|
||||||
|
Cursor,
|
||||||
|
Toplevel,
|
||||||
|
Popup(String),
|
||||||
|
}
|
||||||
|
impl Default for SurfaceID {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Toplevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for SurfaceID {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
deserializer.deserialize_seq(SurfaceIDVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SurfaceIDVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for SurfaceIDVisitor {
|
||||||
|
type Value = SurfaceID;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
f.write_str("idk")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
||||||
|
let Some(discrim) = seq.next_element()? else {
|
||||||
|
return Err(A::Error::missing_field("discrim"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// idk if you wanna check for extraneous elements
|
||||||
|
// I didn't bother
|
||||||
|
|
||||||
|
match discrim {
|
||||||
|
"Cursor" => Ok(SurfaceID::Cursor),
|
||||||
|
"Toplevel" => Ok(SurfaceID::Toplevel),
|
||||||
|
"Popup" => {
|
||||||
|
let Some(text) = seq.next_element()? else {
|
||||||
|
return Err(A::Error::missing_field("popup_text"));
|
||||||
|
};
|
||||||
|
Ok(SurfaceID::Popup(text))
|
||||||
|
}
|
||||||
|
_ => Err(A::Error::unknown_variant(
|
||||||
|
discrim,
|
||||||
|
&["Cursor", "Toplevel", "Popup"],
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for SurfaceID {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Cursor => ["Cursor"].serialize(serializer),
|
||||||
|
Self::Toplevel => ["Toplevel"].serialize(serializer),
|
||||||
|
Self::Popup(text) => ["Popup", text].serialize(serializer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
|
#[serde(tag = "type", content = "content")]
|
||||||
|
pub enum RecommendedState {
|
||||||
|
Maximize(bool),
|
||||||
|
Fullscreen(bool),
|
||||||
|
Minimize,
|
||||||
|
Move,
|
||||||
|
Resize(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PanelItem {
|
||||||
|
pub uid: String,
|
||||||
|
node: Weak<Node>,
|
||||||
|
cursor: Mutex<Option<WlWeak<WlSurface>>>,
|
||||||
|
pub seat_data: Arc<SeatData>,
|
||||||
|
toplevel: WlWeak<XdgToplevel>,
|
||||||
|
popups: Mutex<FxHashMap<String, WlWeak<XdgPopup>>>,
|
||||||
|
pointer_grab: Mutex<Option<SurfaceID>>,
|
||||||
|
keyboard_grab: Mutex<Option<SurfaceID>>,
|
||||||
|
}
|
||||||
|
impl PanelItem {
|
||||||
|
pub fn create(
|
||||||
|
toplevel: XdgToplevel,
|
||||||
|
wl_surface: WlSurface,
|
||||||
|
client_credentials: Option<Credentials>,
|
||||||
|
seat_data: Arc<SeatData>,
|
||||||
|
) -> (Arc<Node>, Arc<PanelItem>) {
|
||||||
|
debug!(?toplevel, ?client_credentials, "Create panel item");
|
||||||
|
|
||||||
|
let startup_settings = client_credentials
|
||||||
|
.and_then(|cred| get_env(cred.pid).ok())
|
||||||
|
.and_then(|env| startup_settings(&env));
|
||||||
|
|
||||||
|
let uid = nanoid!();
|
||||||
|
let node = Arc::new(Node::create(
|
||||||
|
&INTERNAL_CLIENT,
|
||||||
|
"/item/panel/item",
|
||||||
|
&uid,
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
||||||
|
let panel_item = Arc::new(PanelItem {
|
||||||
|
uid: uid.clone(),
|
||||||
|
node: Arc::downgrade(&node),
|
||||||
|
cursor: Mutex::new(None),
|
||||||
|
seat_data,
|
||||||
|
toplevel: toplevel.downgrade(),
|
||||||
|
popups: Mutex::new(FxHashMap::default()),
|
||||||
|
pointer_grab: Mutex::new(None),
|
||||||
|
keyboard_grab: Mutex::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(startup_settings) = &startup_settings {
|
||||||
|
spatial.set_local_transform(
|
||||||
|
spatial.global_transform().inverse() * startup_settings.transform,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
panel_item
|
||||||
|
.seat_data
|
||||||
|
.new_surface(&wl_surface, Arc::downgrade(&panel_item));
|
||||||
|
|
||||||
|
let item = Item::add_to(
|
||||||
|
&node,
|
||||||
|
uid,
|
||||||
|
&ITEM_TYPE_INFO_PANEL,
|
||||||
|
ItemType::Panel(panel_item.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(startup_settings) = &startup_settings {
|
||||||
|
if let Some(acceptor) = startup_settings
|
||||||
|
.acceptors
|
||||||
|
.get(&*ITEM_TYPE_INFO_PANEL)
|
||||||
|
.and_then(|acc| acc.upgrade())
|
||||||
|
{
|
||||||
|
items::capture(&item, &acceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.add_local_signal(
|
||||||
|
"apply_surface_material",
|
||||||
|
PanelItem::apply_surface_material_flex,
|
||||||
|
);
|
||||||
|
node.add_local_signal("configure_toplevel", PanelItem::configure_toplevel_flex);
|
||||||
|
node.add_local_signal(
|
||||||
|
"set_toplevel_capabilities",
|
||||||
|
PanelItem::set_toplevel_capabilities_flex,
|
||||||
|
);
|
||||||
|
node.add_local_signal("pointer_scroll", PanelItem::pointer_scroll_flex);
|
||||||
|
node.add_local_signal("pointer_button", PanelItem::pointer_button_flex);
|
||||||
|
node.add_local_signal("pointer_motion", PanelItem::pointer_motion_flex);
|
||||||
|
|
||||||
|
node.add_local_signal(
|
||||||
|
"keyboard_set_keymap_string",
|
||||||
|
PanelItem::keyboard_set_keymap_string_flex,
|
||||||
|
);
|
||||||
|
node.add_local_signal(
|
||||||
|
"keyboard_set_keymap_names",
|
||||||
|
PanelItem::keyboard_set_keymap_names_flex,
|
||||||
|
);
|
||||||
|
node.add_local_signal("keyboard_key", PanelItem::keyboard_key_flex);
|
||||||
|
|
||||||
|
(node, panel_item)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_node(node: &Node) -> Option<Arc<PanelItem>> {
|
||||||
|
let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None};
|
||||||
|
Some(panel_item.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toplevel(&self) -> XdgToplevel {
|
||||||
|
self.toplevel.upgrade().unwrap()
|
||||||
|
}
|
||||||
|
fn toplevel_xdg_surface(&self) -> XdgSurface {
|
||||||
|
let toplevel = self.toplevel();
|
||||||
|
let data = ToplevelData::get(&toplevel).lock();
|
||||||
|
data.xdg_surface()
|
||||||
|
}
|
||||||
|
fn toplevel_wl_surface(&self) -> WlSurface {
|
||||||
|
XdgSurfaceData::get(&self.toplevel_xdg_surface())
|
||||||
|
.lock()
|
||||||
|
.wl_surface()
|
||||||
|
}
|
||||||
|
fn core_surface(&self) -> Option<Arc<CoreSurface>> {
|
||||||
|
compositor::with_states(&self.toplevel_wl_surface(), |data| {
|
||||||
|
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn flush_clients(&self) {
|
||||||
|
if let Some(core_surface) = self.core_surface() {
|
||||||
|
core_surface.flush_clients();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
||||||
|
match id {
|
||||||
|
SurfaceID::Cursor => self.cursor.lock().clone()?.upgrade().ok(),
|
||||||
|
SurfaceID::Toplevel => Some(self.toplevel_wl_surface()),
|
||||||
|
SurfaceID::Popup(popup) => {
|
||||||
|
let popups = self.popups.lock();
|
||||||
|
let popup = popups.get(popup)?.upgrade().ok()?;
|
||||||
|
let surf = PopupData::get(&popup).lock().wl_surface();
|
||||||
|
Some(surf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn wl_surface_from_id_result(&self, id: &SurfaceID) -> Result<WlSurface> {
|
||||||
|
self.wl_surface_from_id(id)
|
||||||
|
.ok_or(eyre!("Surface with ID not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_surface_material_flex(
|
||||||
|
node: &Node,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct SurfaceMaterialInfo<'a> {
|
||||||
|
surface: SurfaceID,
|
||||||
|
model_node_path: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
let info: SurfaceMaterialInfo = deserialize(data)?;
|
||||||
|
|
||||||
|
let Some(wl_surface) = panel_item.wl_surface_from_id(&info.surface) else { return Ok(()) };
|
||||||
|
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else { return Ok(()) };
|
||||||
|
|
||||||
|
let model_node = calling_client
|
||||||
|
.scenegraph
|
||||||
|
.get_node(info.model_node_path)
|
||||||
|
.ok_or_else(|| eyre!("Model node not found"))?;
|
||||||
|
let Some(Drawable::ModelPart(model_node)) = model_node.drawable.get() else {bail!("Node is not a model")};
|
||||||
|
debug!(?info, "Apply surface material");
|
||||||
|
|
||||||
|
core_surface.apply_material(model_node);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_motion_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
let (surface_id, position): (SurfaceID, Vector2<f64>) = deserialize(data)?;
|
||||||
|
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
|
||||||
|
debug!(?surface_id, ?position, "Pointer deactivate");
|
||||||
|
|
||||||
|
panel_item
|
||||||
|
.seat_data
|
||||||
|
.pointer_event(&wl_surface, PointerEvent::Motion(position));
|
||||||
|
panel_item.flush_clients();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn pointer_button_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(data)?;
|
||||||
|
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
|
||||||
|
debug!(?surface_id, button, state, "Pointer button");
|
||||||
|
|
||||||
|
panel_item
|
||||||
|
.seat_data
|
||||||
|
.pointer_event(&wl_surface, PointerEvent::Button { button, state });
|
||||||
|
panel_item.flush_clients();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn pointer_scroll_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PointerScrollInfo {
|
||||||
|
surface_id: SurfaceID,
|
||||||
|
axis_continuous: Option<Vector2<f32>>,
|
||||||
|
axis_discrete: Option<Vector2<f32>>,
|
||||||
|
}
|
||||||
|
let info: PointerScrollInfo = deserialize(data)?;
|
||||||
|
let wl_surface = panel_item.wl_surface_from_id_result(&info.surface_id)?;
|
||||||
|
|
||||||
|
debug!(?info, "Pointer scroll");
|
||||||
|
|
||||||
|
panel_item.seat_data.pointer_event(
|
||||||
|
&wl_surface,
|
||||||
|
PointerEvent::Scroll {
|
||||||
|
axis_continuous: info.axis_continuous,
|
||||||
|
axis_discrete: info.axis_discrete,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
panel_item.flush_clients();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_set_keymap_string_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let context = xkb::Context::new(0);
|
||||||
|
let keymap =
|
||||||
|
Keymap::new_from_string(&context, deserialize(data)?, XKB_KEYMAP_FORMAT_TEXT_V1, 0)
|
||||||
|
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
||||||
|
|
||||||
|
PanelItem::keyboard_set_keymap_flex(node, &keymap)
|
||||||
|
}
|
||||||
|
fn keyboard_set_keymap_names_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Names<'a> {
|
||||||
|
rules: &'a str,
|
||||||
|
model: &'a str,
|
||||||
|
layout: &'a str,
|
||||||
|
variant: &'a str,
|
||||||
|
options: Option<String>,
|
||||||
|
}
|
||||||
|
let names: Names = deserialize(data)?;
|
||||||
|
let context = xkb::Context::new(0);
|
||||||
|
let keymap = Keymap::new_from_names(
|
||||||
|
&context,
|
||||||
|
names.rules,
|
||||||
|
names.model,
|
||||||
|
names.layout,
|
||||||
|
names.variant,
|
||||||
|
names.options,
|
||||||
|
XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
)
|
||||||
|
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
||||||
|
|
||||||
|
PanelItem::keyboard_set_keymap_flex(node, &keymap)
|
||||||
|
}
|
||||||
|
fn keyboard_set_keymap_flex(node: &Node, keymap: &Keymap) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
let toplevel = panel_item.toplevel_wl_surface();
|
||||||
|
debug!(?toplevel, "Keyboard set keymap");
|
||||||
|
|
||||||
|
let mut surfaces = vec![toplevel];
|
||||||
|
surfaces.extend(panel_item.popups.lock().values().filter_map(|p| {
|
||||||
|
let popup = p.upgrade().ok()?;
|
||||||
|
let popup_data = PopupData::get(&popup).lock();
|
||||||
|
let xdg_surface = popup_data.xdg_surface();
|
||||||
|
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
|
||||||
|
Some(xdg_surface_data.wl_surface())
|
||||||
|
}));
|
||||||
|
|
||||||
|
panel_item.seat_data.set_keymap(keymap, surfaces);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_key_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(data)?;
|
||||||
|
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
|
||||||
|
debug!(key, state, "Set keyboard key state");
|
||||||
|
|
||||||
|
panel_item
|
||||||
|
.seat_data
|
||||||
|
.keyboard_event(&wl_surface, KeyboardEvent::Key { key, state });
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_toplevel_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
|
||||||
|
let Ok(xdg_toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
|
||||||
|
let xdg_surface = panel_item.toplevel_xdg_surface();
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct ConfigureToplevelInfo {
|
||||||
|
size: Option<Vector2<u32>>,
|
||||||
|
states: Vec<u32>,
|
||||||
|
bounds: Option<Vector2<u32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let info: ConfigureToplevelInfo = deserialize(data)?;
|
||||||
|
debug!(info = ?&info, "Configure toplevel info");
|
||||||
|
if let Some(bounds) = info.bounds {
|
||||||
|
if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE {
|
||||||
|
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let size = info.size.unwrap_or(Vector2::from([0; 2]));
|
||||||
|
xdg_toplevel.configure(
|
||||||
|
size.x as i32,
|
||||||
|
size.y as i32,
|
||||||
|
info.states
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|state| state.to_ne_bytes())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||||
|
core_surface.flush_clients();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toplevel_capabilities_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
||||||
|
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
|
||||||
|
let Ok(xdg_toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
|
||||||
|
if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let xdg_surface = panel_item.toplevel_xdg_surface();
|
||||||
|
|
||||||
|
let capabilities: Vec<u8> = deserialize(data)?;
|
||||||
|
debug!("Set toplevel capabilities");
|
||||||
|
xdg_toplevel.wm_capabilities(capabilities);
|
||||||
|
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||||
|
core_surface.flush_clients();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit_toplevel(&self) {
|
||||||
|
// let mapped_size = self.core_surface().and_then(|c| c.size());
|
||||||
|
let toplevel = self.toplevel();
|
||||||
|
let state = ToplevelData::get(&toplevel);
|
||||||
|
let state = state.lock();
|
||||||
|
// let mut queued_state = state.queued_state.take().unwrap();
|
||||||
|
// queued_state.mapped = mapped_size.is_some();
|
||||||
|
// if let Some(size) = mapped_size {
|
||||||
|
// queued_state.size = size;
|
||||||
|
// queued_state.geometry.update_to_surface_size(size);
|
||||||
|
// }
|
||||||
|
// *state = (*queued_state).clone();
|
||||||
|
// state.queued_state = Some(queued_state);
|
||||||
|
|
||||||
|
debug!(state = ?&*state, "Commit toplevel");
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
let _ = node.send_remote_signal("commit_toplevel", &serialize(&*state).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recommend_toplevel_state(&self, state: RecommendedState) {
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
let data = serialize(state).unwrap();
|
||||||
|
debug!(?state, "Recommend toplevel state");
|
||||||
|
|
||||||
|
let _ = node.send_remote_signal("recommend_toplevel_state", &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_popup(&self, popup: &XdgPopup, data: &PopupData) {
|
||||||
|
let uid = data.uid.clone();
|
||||||
|
|
||||||
|
self.popups.lock().insert(uid.clone(), popup.downgrade());
|
||||||
|
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
let _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap());
|
||||||
|
}
|
||||||
|
// pub fn commit_popup(&self, data: &PopupData) {
|
||||||
|
// let xdg_surf = data.xdg_surface.upgrade().unwrap();
|
||||||
|
// let surf = xdg_surf
|
||||||
|
// .data::<XdgSurfaceData>()
|
||||||
|
// .unwrap()
|
||||||
|
// .wl_surface
|
||||||
|
// .upgrade()
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let core_surface =
|
||||||
|
// compositor::with_states(&surf, |s| s.data_map.get::<Arc<CoreSurface>>().cloned())
|
||||||
|
// .unwrap();
|
||||||
|
// let mut popup_state = data.state.lock();
|
||||||
|
// popup_state.mapped = core_surface.size().is_some();
|
||||||
|
// }
|
||||||
|
pub fn reposition_popup(&self, popup_state: &PopupData) {
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
|
||||||
|
let _ = node.send_remote_signal(
|
||||||
|
"reposition_popup",
|
||||||
|
&serialize(popup_state.positioner_data().unwrap()).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pub fn drop_popup(&self, uid: &str) {
|
||||||
|
if let Some(popup) = self
|
||||||
|
.popups
|
||||||
|
.lock()
|
||||||
|
.remove(uid)
|
||||||
|
.and_then(|popup| popup.upgrade().ok()?.data::<Arc<PopupData>>().cloned())
|
||||||
|
{
|
||||||
|
self.seat_data.drop_surface(&popup.wl_surface());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
let _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
|
||||||
|
let _ = node.send_remote_signal("grab_keyboard", &serialize(sid).unwrap());
|
||||||
|
}
|
||||||
|
pub fn set_cursor(&self, surface: Option<&WlSurface>, hotspot_x: i32, hotspot_y: i32) {
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
debug!(?surface, hotspot_x, hotspot_y, "Set cursor size");
|
||||||
|
let mut data = serialize(()).unwrap();
|
||||||
|
|
||||||
|
let cursor_size = surface
|
||||||
|
.and_then(|c| CoreSurface::from_wl_surface(c))
|
||||||
|
.and_then(|c| c.size());
|
||||||
|
|
||||||
|
if let Some(size) = cursor_size {
|
||||||
|
data = serialize((size, (hotspot_x, hotspot_y))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = node.send_remote_signal("set_cursor", &data);
|
||||||
|
*self.cursor.lock() = surface.map(|surf| surf.downgrade());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_drop(&self) {
|
||||||
|
let toplevel = self.toplevel_wl_surface();
|
||||||
|
self.seat_data.drop_surface(&toplevel);
|
||||||
|
|
||||||
|
debug!("Drop panel item");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ItemSpecialization for PanelItem {
|
||||||
|
fn serialize_start_data(&self, id: &str) -> Vec<u8> {
|
||||||
|
let cursor = self.cursor.lock().as_ref().and_then(|c| c.upgrade().ok());
|
||||||
|
let cursor_size = cursor
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| CoreSurface::from_wl_surface(&c))
|
||||||
|
.and_then(|c| c.size());
|
||||||
|
let cursor_hotspot = cursor
|
||||||
|
.and_then(|c| {
|
||||||
|
compositor::with_states(&c, |data| data.data_map.get::<Arc<Cursor>>().cloned())
|
||||||
|
})
|
||||||
|
.map(|cursor| cursor.hotspot);
|
||||||
|
|
||||||
|
let toplevel = self.toplevel();
|
||||||
|
let toplevel_state = ToplevelData::get(&toplevel);
|
||||||
|
let toplevel_state = toplevel_state.lock().clone();
|
||||||
|
|
||||||
|
let popups = self
|
||||||
|
.popups
|
||||||
|
.lock()
|
||||||
|
.values()
|
||||||
|
.filter_map(|v| Some(v.upgrade().ok()?.data::<Mutex<PopupData>>()?.lock().clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let pointer_grab = self.pointer_grab.lock().clone();
|
||||||
|
let keyboard_grab = self.keyboard_grab.lock().clone();
|
||||||
|
|
||||||
|
serialize((
|
||||||
|
id,
|
||||||
|
(
|
||||||
|
cursor_size.zip(cursor_hotspot),
|
||||||
|
toplevel_state,
|
||||||
|
popups,
|
||||||
|
pointer_grab,
|
||||||
|
keyboard_grab,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for PanelItem {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Dropped panel item, basically just a debug breakpoint place
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
use super::{
|
use super::{
|
||||||
state::{ClientState, WaylandState},
|
panel_item::PanelItem, state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE,
|
||||||
surface::CoreSurface,
|
|
||||||
SERIAL_COUNTER,
|
SERIAL_COUNTER,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::core::task;
|
||||||
core::task,
|
use color_eyre::eyre::Result;
|
||||||
nodes::items::panel::{Backend, Geometry, PanelItem},
|
|
||||||
};
|
|
||||||
use color_eyre::eyre::{bail, eyre, Result};
|
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
@@ -31,51 +27,39 @@ use smithay::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
sync::Arc,
|
sync::{Arc, Weak},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tokio::sync::watch;
|
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keycode, Keymap};
|
use xkbcommon::xkb::{self, Keymap};
|
||||||
|
|
||||||
pub fn handle_cursor<B: Backend>(
|
|
||||||
panel_item: &Arc<PanelItem<B>>,
|
|
||||||
mut cursor: watch::Receiver<Option<CursorInfo>>,
|
|
||||||
) {
|
|
||||||
let panel_item_weak = Arc::downgrade(panel_item);
|
|
||||||
let _ = task::new(|| "cursor handler", async move {
|
|
||||||
while cursor.changed().await.is_ok() {
|
|
||||||
let Some(panel_item) = panel_item_weak.upgrade() else {continue};
|
|
||||||
let cursor_info = cursor.borrow();
|
|
||||||
panel_item.set_cursor(cursor_info.as_ref().and_then(CursorInfo::cursor_data));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KeyboardInfo {
|
pub struct KeyboardInfo {
|
||||||
keymap_string: String,
|
|
||||||
keymap: KeymapFile,
|
keymap: KeymapFile,
|
||||||
state: xkb::State,
|
state: xkb::State,
|
||||||
mods: ModifiersState,
|
mods: ModifiersState,
|
||||||
keys: FxHashSet<u32>,
|
keys: FxHashSet<u32>,
|
||||||
}
|
}
|
||||||
impl KeyboardInfo {
|
impl KeyboardInfo {
|
||||||
pub fn new(keymap_string: String, keymap: &Keymap) -> Self {
|
pub fn new(keymap: &Keymap) -> Self {
|
||||||
KeyboardInfo {
|
KeyboardInfo {
|
||||||
keymap_string,
|
|
||||||
state: xkb::State::new(keymap),
|
state: xkb::State::new(keymap),
|
||||||
keymap: KeymapFile::new(keymap),
|
keymap: KeymapFile::new(keymap),
|
||||||
mods: ModifiersState::default(),
|
mods: ModifiersState::default(),
|
||||||
keys: FxHashSet::default(),
|
keys: FxHashSet::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn process(&mut self, key: u32, pressed: bool, keyboard: &WlKeyboard) -> Result<usize> {
|
pub fn process(&mut self, key: u32, state: u32, keyboard: &WlKeyboard) -> Result<usize> {
|
||||||
let xkb_key_state = if pressed {
|
let wl_key_state = match state {
|
||||||
xkb::KeyDirection::Down
|
0 => KeyState::Released,
|
||||||
} else {
|
1 => KeyState::Pressed,
|
||||||
xkb::KeyDirection::Up
|
_ => color_eyre::eyre::bail!("Invalid key state!"),
|
||||||
};
|
};
|
||||||
let state_components = self.state.update_key(Keycode::new(key + 8), xkb_key_state);
|
let xkb_key_state = match state {
|
||||||
|
0 => xkb::KeyDirection::Up,
|
||||||
|
1 => xkb::KeyDirection::Down,
|
||||||
|
_ => color_eyre::eyre::bail!("Invalid key state!"),
|
||||||
|
};
|
||||||
|
let state_components = self.state.update_key(key + 8, xkb_key_state);
|
||||||
if state_components != 0 {
|
if state_components != 0 {
|
||||||
self.mods.update_with(&self.state);
|
self.mods.update_with(&self.state);
|
||||||
keyboard.modifiers(
|
keyboard.modifiers(
|
||||||
@@ -86,17 +70,6 @@ impl KeyboardInfo {
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// if pressed {
|
|
||||||
// println!("Key {key} is being pressed with {state_components} modifiers");
|
|
||||||
// } else {
|
|
||||||
// println!("Key {key} is being released with {state_components} modifiers");
|
|
||||||
// }
|
|
||||||
|
|
||||||
let wl_key_state = if pressed {
|
|
||||||
KeyState::Pressed
|
|
||||||
} else {
|
|
||||||
KeyState::Released
|
|
||||||
};
|
|
||||||
keyboard.key(SERIAL_COUNTER.inc(), 0, key, wl_key_state);
|
keyboard.key(SERIAL_COUNTER.inc(), 0, key, wl_key_state);
|
||||||
match wl_key_state {
|
match wl_key_state {
|
||||||
KeyState::Pressed => {
|
KeyState::Pressed => {
|
||||||
@@ -114,7 +87,7 @@ unsafe impl Send for KeyboardInfo {}
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PointerEvent {
|
pub enum PointerEvent {
|
||||||
Motion(Vector2<f32>),
|
Motion(Vector2<f64>),
|
||||||
Button {
|
Button {
|
||||||
button: u32,
|
button: u32,
|
||||||
state: u32,
|
state: u32,
|
||||||
@@ -127,36 +100,29 @@ pub enum PointerEvent {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum KeyboardEvent {
|
pub enum KeyboardEvent {
|
||||||
Keymap,
|
Keymap,
|
||||||
Key { key: u32, state: bool },
|
Key { key: u32, state: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_millis(50);
|
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
struct SurfaceInfo {
|
struct SurfaceInfo {
|
||||||
wl_surface: WlWeak<WlSurface>,
|
wl_surface: WlWeak<WlSurface>,
|
||||||
cursor_sender: watch::Sender<Option<CursorInfo>>,
|
panel_item: Weak<PanelItem>,
|
||||||
pointer_queue: VecDeque<PointerEvent>,
|
pointer_queue: VecDeque<PointerEvent>,
|
||||||
pointer_latest_event: Instant,
|
pointer_latest_event: Instant,
|
||||||
keyboard_queue: VecDeque<KeyboardEvent>,
|
keyboard_queue: VecDeque<KeyboardEvent>,
|
||||||
keyboard_info: Option<KeyboardInfo>,
|
keyboard_info: Option<KeyboardInfo>,
|
||||||
}
|
}
|
||||||
impl SurfaceInfo {
|
impl SurfaceInfo {
|
||||||
fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender<Option<CursorInfo>>) -> Self {
|
fn new(wl_surface: &WlSurface, panel_item: Weak<PanelItem>) -> Self {
|
||||||
SurfaceInfo {
|
SurfaceInfo {
|
||||||
wl_surface: wl_surface.downgrade(),
|
wl_surface: wl_surface.downgrade(),
|
||||||
cursor_sender,
|
panel_item,
|
||||||
pointer_queue: VecDeque::new(),
|
pointer_queue: VecDeque::new(),
|
||||||
pointer_latest_event: Instant::now(),
|
pointer_latest_event: Instant::now(),
|
||||||
keyboard_queue: VecDeque::new(),
|
keyboard_queue: VecDeque::new(),
|
||||||
keyboard_info: None,
|
keyboard_info: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn flush(&self) {
|
|
||||||
if let Some(client) = self.wl_surface.upgrade().ok().and_then(|s| s.client()) {
|
|
||||||
if let Some(client_data) = client.get_data::<ClientState>() {
|
|
||||||
client_data.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
|
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
|
||||||
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
|
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
|
||||||
let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; };
|
let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; };
|
||||||
@@ -171,20 +137,18 @@ impl SurfaceInfo {
|
|||||||
pointer.enter(
|
pointer.enter(
|
||||||
SERIAL_COUNTER.inc(),
|
SERIAL_COUNTER.inc(),
|
||||||
&focus,
|
&focus,
|
||||||
(pos.x as f64).clamp(0.0, focus_size.x as f64),
|
pos.x.clamp(0.0, focus_size.x as f64),
|
||||||
(pos.y as f64).clamp(0.0, focus_size.y as f64),
|
pos.y.clamp(0.0, focus_size.y as f64),
|
||||||
);
|
);
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
(true, PointerEvent::Motion(pos)) => {
|
(true, PointerEvent::Motion(pos)) => {
|
||||||
pointer.motion(
|
pointer.motion(
|
||||||
0,
|
0,
|
||||||
(pos.x as f64).clamp(0.0, focus_size.x as f64),
|
pos.x.clamp(0.0, focus_size.x as f64),
|
||||||
(pos.y as f64).clamp(0.0, focus_size.y as f64),
|
pos.y.clamp(0.0, focus_size.y as f64),
|
||||||
);
|
);
|
||||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
pointer.frame();
|
||||||
pointer.frame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(true, PointerEvent::Button { button, state }) => {
|
(true, PointerEvent::Button { button, state }) => {
|
||||||
pointer.button(
|
pointer.button(
|
||||||
@@ -197,9 +161,7 @@ impl SurfaceInfo {
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
pointer.frame();
|
||||||
pointer.frame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
true,
|
true,
|
||||||
@@ -210,24 +172,17 @@ impl SurfaceInfo {
|
|||||||
) => {
|
) => {
|
||||||
if let Some(axis_continuous) = axis_continuous {
|
if let Some(axis_continuous) = axis_continuous {
|
||||||
pointer.axis(0, Axis::HorizontalScroll, axis_continuous.x as f64);
|
pointer.axis(0, Axis::HorizontalScroll, axis_continuous.x as f64);
|
||||||
pointer.axis(0, Axis::VerticalScroll, -axis_continuous.y as f64);
|
pointer.axis(0, Axis::VerticalScroll, axis_continuous.y as f64);
|
||||||
}
|
}
|
||||||
if pointer.version() >= wl_pointer::EVT_AXIS_DISCRETE_SINCE {
|
if let Some(axis_discrete) = axis_discrete {
|
||||||
if let Some(axis_discrete) = axis_discrete {
|
pointer.axis_discrete(Axis::HorizontalScroll, axis_discrete.x as i32);
|
||||||
pointer.axis_discrete(Axis::HorizontalScroll, axis_discrete.x as i32);
|
pointer.axis_discrete(Axis::VerticalScroll, axis_discrete.y as i32);
|
||||||
pointer.axis_discrete(Axis::VerticalScroll, -axis_discrete.y as i32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if pointer.version() >= wl_pointer::EVT_AXIS_STOP_SINCE
|
if axis_discrete.is_none() && axis_continuous.is_none() {
|
||||||
&& axis_discrete.is_none()
|
|
||||||
&& axis_continuous.is_none()
|
|
||||||
{
|
|
||||||
pointer.axis_stop(0, Axis::HorizontalScroll);
|
pointer.axis_stop(0, Axis::HorizontalScroll);
|
||||||
pointer.axis_stop(0, Axis::VerticalScroll);
|
pointer.axis_stop(0, Axis::VerticalScroll);
|
||||||
}
|
}
|
||||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
pointer.frame();
|
||||||
pointer.frame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(locked, event) => {
|
(locked, event) => {
|
||||||
warn!(locked, ?event, "Invalid pointer event!");
|
warn!(locked, ?event, "Invalid pointer event!");
|
||||||
@@ -238,7 +193,6 @@ impl SurfaceInfo {
|
|||||||
pointer.leave(SERIAL_COUNTER.inc(), &focus);
|
pointer.leave(SERIAL_COUNTER.inc(), &focus);
|
||||||
locked = false;
|
locked = false;
|
||||||
}
|
}
|
||||||
self.flush();
|
|
||||||
|
|
||||||
locked
|
locked
|
||||||
}
|
}
|
||||||
@@ -248,9 +202,7 @@ impl SurfaceInfo {
|
|||||||
|
|
||||||
if !locked {
|
if !locked {
|
||||||
keyboard.enter(0, &focus, vec![]);
|
keyboard.enter(0, &focus, vec![]);
|
||||||
if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE {
|
keyboard.repeat_info(0, 0);
|
||||||
keyboard.repeat_info(0, 0);
|
|
||||||
}
|
|
||||||
locked = info.keymap.send(keyboard).is_ok();
|
locked = info.keymap.send(keyboard).is_ok();
|
||||||
}
|
}
|
||||||
while let Some(event) = self.keyboard_queue.pop_front() {
|
while let Some(event) = self.keyboard_queue.pop_front() {
|
||||||
@@ -272,62 +224,50 @@ impl SurfaceInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.flush();
|
|
||||||
locked
|
locked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SeatData {
|
pub struct SeatData {
|
||||||
pub client: OnceCell<ClientId>,
|
client: ClientId,
|
||||||
global_id: OnceCell<GlobalId>,
|
global_id: OnceCell<GlobalId>,
|
||||||
surfaces: Mutex<FxHashMap<ObjectId, SurfaceInfo>>,
|
surfaces: Mutex<FxHashMap<ObjectId, SurfaceInfo>>,
|
||||||
pointer: OnceCell<(WlPointer, Mutex<ObjectId>)>,
|
pointer: OnceCell<(WlPointer, Mutex<ObjectId>)>,
|
||||||
keyboard: OnceCell<(WlKeyboard, Mutex<ObjectId>)>,
|
keyboard: OnceCell<(WlKeyboard, Mutex<ObjectId>)>,
|
||||||
touch: OnceCell<WlTouch>,
|
touch: OnceCell<WlTouch>,
|
||||||
touches: Mutex<FxHashMap<ObjectId, u32>>,
|
|
||||||
}
|
}
|
||||||
impl SeatData {
|
impl SeatData {
|
||||||
pub fn new(dh: &DisplayHandle) -> Arc<Self> {
|
pub fn new(dh: &DisplayHandle, client: ClientId) -> Arc<Self> {
|
||||||
let seat_data = Arc::new(SeatData {
|
let seat_data = Arc::new(SeatData {
|
||||||
client: OnceCell::new(),
|
client,
|
||||||
global_id: OnceCell::new(),
|
global_id: OnceCell::new(),
|
||||||
surfaces: Mutex::new(FxHashMap::default()),
|
surfaces: Mutex::new(FxHashMap::default()),
|
||||||
pointer: OnceCell::new(),
|
pointer: OnceCell::new(),
|
||||||
keyboard: OnceCell::new(),
|
keyboard: OnceCell::new(),
|
||||||
touch: OnceCell::new(),
|
touch: OnceCell::new(),
|
||||||
touches: Mutex::new(FxHashMap::default()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = seat_data
|
seat_data
|
||||||
.global_id
|
.global_id
|
||||||
.set(dh.create_global::<WaylandState, _, _>(7, seat_data.clone()));
|
.set(dh.create_global::<WaylandState, _, _>(7, seat_data.clone()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
seat_data
|
seat_data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_keymap(&self, keymap_str: String, surfaces: Vec<WlSurface>) -> Result<()> {
|
pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec<WlSurface>) {
|
||||||
let context = xkb::Context::new(0);
|
|
||||||
let keymap =
|
|
||||||
Keymap::new_from_string(&context, keymap_str.clone(), XKB_KEYMAP_FORMAT_TEXT_V1, 0)
|
|
||||||
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
|
||||||
let mut panels = self.surfaces.lock();
|
let mut panels = self.surfaces.lock();
|
||||||
let Some((_, focus)) = self.keyboard.get() else {bail!("Could not get keyboard")};
|
let Some((_, focus)) = self.keyboard.get() else {return};
|
||||||
for surface in surfaces {
|
for surface in surfaces {
|
||||||
let Some(surface_info) = panels.get_mut(&surface.id()) else {continue};
|
let Some(surface_info) = panels.get_mut(&surface.id()) else {continue};
|
||||||
if let Some(keyboard_info) = &mut surface_info.keyboard_info {
|
|
||||||
if &keyboard_info.keymap_string == &keymap_str {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
surface_info
|
surface_info
|
||||||
.keyboard_info
|
.keyboard_info
|
||||||
.replace(KeyboardInfo::new(keymap_str.clone(), &keymap));
|
.replace(KeyboardInfo::new(keymap));
|
||||||
|
|
||||||
if *focus.lock() == surface.id() {
|
if *focus.lock() == surface.id() {
|
||||||
surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap);
|
surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) {
|
pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) {
|
||||||
@@ -403,13 +343,10 @@ impl SeatData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_surface(&self, surface: &WlSurface) -> watch::Receiver<Option<CursorInfo>> {
|
pub fn new_surface(&self, surface: &WlSurface, panel_item: Weak<PanelItem>) {
|
||||||
let (tx, rx) = watch::channel(None);
|
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.lock()
|
.lock()
|
||||||
.insert(surface.id(), SurfaceInfo::new(surface, tx));
|
.insert(surface.id(), SurfaceInfo::new(surface, panel_item));
|
||||||
|
|
||||||
rx
|
|
||||||
}
|
}
|
||||||
pub fn drop_surface(&self, surface: &WlSurface) {
|
pub fn drop_surface(&self, surface: &WlSurface) {
|
||||||
self.surfaces.lock().remove(&surface.id());
|
self.surfaces.lock().remove(&surface.id());
|
||||||
@@ -425,51 +362,14 @@ impl SeatData {
|
|||||||
*keyboard_focus = ObjectId::null();
|
*keyboard_focus = ObjectId::null();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.touches.lock().remove(&surface.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn touch_down(&self, surface: &WlSurface, id: u32, position: Vector2<f32>) {
|
|
||||||
let Some(touch) = self.touch.get() else {return};
|
|
||||||
touch.down(
|
|
||||||
SERIAL_COUNTER.inc(),
|
|
||||||
0,
|
|
||||||
surface,
|
|
||||||
id as i32,
|
|
||||||
position.x as f64,
|
|
||||||
position.y as f64,
|
|
||||||
);
|
|
||||||
self.touches.lock().insert(surface.id(), id);
|
|
||||||
}
|
|
||||||
pub fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
|
||||||
let Some(touch) = self.touch.get() else {return};
|
|
||||||
touch.motion(0, id as i32, position.x as f64, position.y as f64);
|
|
||||||
}
|
|
||||||
pub fn touch_up(&self, id: u32) {
|
|
||||||
let Some(touch) = self.touch.get() else {return};
|
|
||||||
touch.up(SERIAL_COUNTER.inc(), 0, id as i32);
|
|
||||||
let mut touches = self.touches.lock();
|
|
||||||
touches.retain(|_, tid| *tid != id);
|
|
||||||
}
|
|
||||||
pub fn reset_touches(&self) {
|
|
||||||
let Some(touch) = self.touch.get() else {return};
|
|
||||||
for (_, touch_id) in self.touches.lock().drain() {
|
|
||||||
touch.up(SERIAL_COUNTER.inc(), 0, touch_id as i32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Drop for SeatData {
|
||||||
pub struct CursorInfo {
|
fn drop(&mut self) {
|
||||||
pub surface: WlWeak<WlSurface>,
|
let id = self.global_id.take().unwrap();
|
||||||
pub hotspot_x: i32,
|
let _ = task::new(|| "global destroy queue garbage collection", async move {
|
||||||
pub hotspot_y: i32,
|
GLOBAL_DESTROY_QUEUE.get().unwrap().send(id).await
|
||||||
}
|
});
|
||||||
impl CursorInfo {
|
|
||||||
pub fn cursor_data(&self) -> Option<Geometry> {
|
|
||||||
let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?;
|
|
||||||
Some(Geometry {
|
|
||||||
origin: [self.hotspot_x, self.hotspot_y].into(),
|
|
||||||
size: cursor_size,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,12 +388,11 @@ impl GlobalDispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
resource.name(nanoid!());
|
resource.name(nanoid!());
|
||||||
}
|
}
|
||||||
|
|
||||||
resource.capabilities(Capability::Pointer | Capability::Keyboard | Capability::Touch);
|
resource.capabilities(Capability::Pointer | Capability::Keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_view(client: Client, data: &Arc<SeatData>) -> bool {
|
fn can_view(client: Client, data: &Arc<SeatData>) -> bool {
|
||||||
let Some(seat_client) = data.client.get().cloned() else {return false};
|
client.id() == data.client
|
||||||
client.id() == seat_client
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,9 +413,7 @@ impl Dispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
}
|
}
|
||||||
wl_seat::Request::GetKeyboard { id } => {
|
wl_seat::Request::GetKeyboard { id } => {
|
||||||
let keyboard = data_init.init(id, data.clone());
|
let keyboard = data_init.init(id, data.clone());
|
||||||
if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE {
|
keyboard.repeat_info(0, 0);
|
||||||
keyboard.repeat_info(0, 0);
|
|
||||||
}
|
|
||||||
let _ = data.keyboard.set((keyboard, Mutex::new(ObjectId::null())));
|
let _ = data.keyboard.set((keyboard, Mutex::new(ObjectId::null())));
|
||||||
}
|
}
|
||||||
wl_seat::Request::GetTouch { id } => {
|
wl_seat::Request::GetTouch { id } => {
|
||||||
@@ -528,9 +425,12 @@ impl Dispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Cursor {
|
||||||
|
pub hotspot: Vector2<i32>,
|
||||||
|
}
|
||||||
impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
||||||
fn request(
|
fn request(
|
||||||
_state: &mut WaylandState,
|
state: &mut WaylandState,
|
||||||
_client: &Client,
|
_client: &Client,
|
||||||
_resource: &WlPointer,
|
_resource: &WlPointer,
|
||||||
request: wl_pointer::Request,
|
request: wl_pointer::Request,
|
||||||
@@ -546,8 +446,16 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
hotspot_y,
|
hotspot_y,
|
||||||
} => {
|
} => {
|
||||||
if let Some(surface) = surface.as_ref() {
|
if let Some(surface) = surface.as_ref() {
|
||||||
CoreSurface::add_to(dh.clone(), surface, || (), |_| ());
|
CoreSurface::add_to(&state.display, dh.clone(), surface, |_| ());
|
||||||
compositor::with_states(surface, |data| {
|
compositor::with_states(surface, |data| {
|
||||||
|
data.data_map.insert_if_missing_threadsafe(|| {
|
||||||
|
Arc::new(Mutex::new(Cursor {
|
||||||
|
hotspot: Vector2::from([hotspot_x, hotspot_y]),
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
let mut cursor = data.data_map.get::<Arc<Mutex<Cursor>>>().unwrap().lock();
|
||||||
|
cursor.hotspot = Vector2::from([hotspot_x, hotspot_y]);
|
||||||
|
|
||||||
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
||||||
core_surface.set_material_offset(1);
|
core_surface.set_material_offset(1);
|
||||||
}
|
}
|
||||||
@@ -558,12 +466,8 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
let focus = focus.lock();
|
let focus = focus.lock();
|
||||||
let surfaces = seat_data.surfaces.lock();
|
let surfaces = seat_data.surfaces.lock();
|
||||||
let Some(surface_info) = surfaces.get(&focus) else {return};
|
let Some(surface_info) = surfaces.get(&focus) else {return};
|
||||||
let cursor_info = surface.map(|surface| CursorInfo {
|
let Some(panel_item) = surface_info.panel_item.upgrade() else {return};
|
||||||
surface: surface.downgrade(),
|
panel_item.set_cursor(surface.as_ref(), hotspot_x, hotspot_y);
|
||||||
hotspot_x,
|
|
||||||
hotspot_y,
|
|
||||||
});
|
|
||||||
let _ = surface_info.cursor_sender.send_replace(cursor_info);
|
|
||||||
}
|
}
|
||||||
wl_pointer::Request::Release => (),
|
wl_pointer::Request::Release => (),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|||||||
7
src/wayland/shaders/mod.rs
Normal file
7
src/wayland/shaders/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
// Basic gamma correction shader
|
||||||
|
// pub const PANEL_SHADER_BYTES: &[u8] = include_bytes!("shader_unlit_gamma.sks");
|
||||||
|
|
||||||
|
// Simula shader with fancy lanzcos sampling
|
||||||
|
pub const PANEL_SHADER_BYTES: &[u8] = include_bytes!("shader_unlit_simula.sks");
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
use crate::wayland::seat::SeatData;
|
use crate::wayland::seat::SeatData;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{allocator::dmabuf::Dmabuf, egl::EGLDevice, renderer::gles::GlesRenderer},
|
backend::{
|
||||||
|
allocator::dmabuf::Dmabuf,
|
||||||
|
renderer::{gles::GlesRenderer, ImportDma},
|
||||||
|
},
|
||||||
delegate_dmabuf, delegate_output, delegate_shm,
|
delegate_dmabuf, delegate_output, delegate_shm,
|
||||||
output::{Mode, Output, Scale, Subpixel},
|
output::{Mode, Output, Scale, Subpixel},
|
||||||
reexports::{
|
reexports::{
|
||||||
@@ -14,43 +17,28 @@ use smithay::{
|
|||||||
wayland_server::{
|
wayland_server::{
|
||||||
backend::{ClientData, ClientId, DisconnectReason},
|
backend::{ClientData, ClientId, DisconnectReason},
|
||||||
protocol::{wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager},
|
protocol::{wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager},
|
||||||
DisplayHandle,
|
Display, DisplayHandle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::{Size, Transform},
|
utils::{Size, Transform},
|
||||||
wayland::{
|
wayland::{
|
||||||
buffer::BufferHandler,
|
buffer::BufferHandler,
|
||||||
compositor::{CompositorClientState, CompositorState},
|
compositor::{CompositorClientState, CompositorState},
|
||||||
dmabuf::{
|
dmabuf::{self, DmabufGlobal, DmabufHandler, DmabufState},
|
||||||
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
|
||||||
ImportError,
|
|
||||||
},
|
|
||||||
shell::kde::decoration::KdeDecorationState,
|
shell::kde::decoration::KdeDecorationState,
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tracing::info;
|
||||||
use tracing::{info, warn};
|
|
||||||
|
|
||||||
use super::DisplayWrapper;
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub id: OnceCell<ClientId>,
|
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
pub display: Weak<DisplayWrapper>,
|
|
||||||
pub seat: Arc<SeatData>,
|
|
||||||
}
|
|
||||||
impl ClientState {
|
|
||||||
pub fn flush(&self) {
|
|
||||||
let Some(display) = self.display.upgrade() else {return};
|
|
||||||
let _ = display.flush_clients(self.id.get().cloned());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl ClientData for ClientState {
|
impl ClientData for ClientState {
|
||||||
fn initialized(&self, client_id: ClientId) {
|
fn initialized(&self, client_id: ClientId) {
|
||||||
info!("Wayland client {:?} connected", client_id);
|
info!("Wayland client {:?} connected", client_id);
|
||||||
let _ = self.id.set(client_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disconnected(&self, client_id: ClientId, reason: DisconnectReason) {
|
fn disconnected(&self, client_id: ClientId, reason: DisconnectReason) {
|
||||||
@@ -63,66 +51,36 @@ impl ClientData for ClientState {
|
|||||||
|
|
||||||
pub struct WaylandState {
|
pub struct WaylandState {
|
||||||
pub weak_ref: Weak<Mutex<WaylandState>>,
|
pub weak_ref: Weak<Mutex<WaylandState>>,
|
||||||
|
pub display: Arc<Mutex<Display<WaylandState>>>,
|
||||||
pub display_handle: DisplayHandle,
|
pub display_handle: DisplayHandle,
|
||||||
|
|
||||||
pub compositor_state: CompositorState,
|
pub compositor_state: CompositorState,
|
||||||
// pub xdg_activation_state: XdgActivationState,
|
// pub xdg_activation_state: XdgActivationState,
|
||||||
pub kde_decoration_state: KdeDecorationState,
|
pub kde_decoration_state: KdeDecorationState,
|
||||||
pub shm_state: ShmState,
|
pub shm_state: ShmState,
|
||||||
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
pub dmabuf_state: DmabufState,
|
||||||
dmabuf_tx: UnboundedSender<Dmabuf>,
|
pub dmabuf_global: DmabufGlobal,
|
||||||
|
pub pending_dmabufs: Vec<Dmabuf>,
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
|
pub seats: FxHashMap<ClientId, Arc<SeatData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandState {
|
impl WaylandState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
display: Arc<Mutex<Display<WaylandState>>>,
|
||||||
display_handle: DisplayHandle,
|
display_handle: DisplayHandle,
|
||||||
renderer: &GlesRenderer,
|
renderer: &GlesRenderer,
|
||||||
dmabuf_tx: UnboundedSender<Dmabuf>,
|
|
||||||
) -> 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);
|
||||||
let kde_decoration_state =
|
let kde_decoration_state =
|
||||||
KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server);
|
KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server);
|
||||||
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 mut dmabuf_state = DmabufState::new();
|
||||||
.and_then(|device| device.try_get_render_node());
|
let dmabuf_global = dmabuf_state.create_global::<Self>(
|
||||||
|
&display_handle,
|
||||||
let dmabuf_formats = renderer
|
renderer.dmabuf_formats().collect::<Vec<_>>(),
|
||||||
.egl_context()
|
);
|
||||||
.dmabuf_render_formats()
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let dmabuf_default_feedback = match render_node {
|
|
||||||
Ok(Some(node)) => DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats.clone())
|
|
||||||
.build()
|
|
||||||
.ok(),
|
|
||||||
Ok(None) => {
|
|
||||||
warn!("failed to query render node, dmabuf will use v3");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(?err, "failed to egl device for display, dmabuf will use v3");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// if we failed to build dmabuf feedback we fall back to dmabuf v3
|
|
||||||
// Note: egl on Mesa requires either v4 or wl_drm (initialized with bind_wl_display)
|
|
||||||
let dmabuf_state = if let Some(default_feedback) = dmabuf_default_feedback {
|
|
||||||
let mut dmabuf_state = DmabufState::new();
|
|
||||||
let dmabuf_global = dmabuf_state.create_global_with_default_feedback::<WaylandState>(
|
|
||||||
&display_handle,
|
|
||||||
&default_feedback,
|
|
||||||
);
|
|
||||||
(dmabuf_state, dmabuf_global, Some(default_feedback))
|
|
||||||
} else {
|
|
||||||
let mut dmabuf_state = DmabufState::new();
|
|
||||||
let dmabuf_global =
|
|
||||||
dmabuf_state.create_global::<WaylandState>(&display_handle, dmabuf_formats.clone());
|
|
||||||
(dmabuf_state, dmabuf_global, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = Output::new(
|
let output = Output::new(
|
||||||
"1x".to_owned(),
|
"1x".to_owned(),
|
||||||
smithay::output::PhysicalProperties {
|
smithay::output::PhysicalProperties {
|
||||||
@@ -134,7 +92,7 @@ impl WaylandState {
|
|||||||
);
|
);
|
||||||
let _output_global = output.create_global::<Self>(&display_handle);
|
let _output_global = output.create_global::<Self>(&display_handle);
|
||||||
let mode = Mode {
|
let mode = Mode {
|
||||||
size: (2048, 2048).into(),
|
size: (4096, 4096).into(),
|
||||||
refresh: 60000,
|
refresh: 60000,
|
||||||
};
|
};
|
||||||
output.change_current_state(
|
output.change_current_state(
|
||||||
@@ -153,6 +111,7 @@ impl WaylandState {
|
|||||||
Arc::new_cyclic(|weak| {
|
Arc::new_cyclic(|weak| {
|
||||||
Mutex::new(WaylandState {
|
Mutex::new(WaylandState {
|
||||||
weak_ref: weak.clone(),
|
weak_ref: weak.clone(),
|
||||||
|
display,
|
||||||
display_handle,
|
display_handle,
|
||||||
|
|
||||||
compositor_state,
|
compositor_state,
|
||||||
@@ -160,11 +119,18 @@ impl WaylandState {
|
|||||||
kde_decoration_state,
|
kde_decoration_state,
|
||||||
shm_state,
|
shm_state,
|
||||||
dmabuf_state,
|
dmabuf_state,
|
||||||
dmabuf_tx,
|
dmabuf_global,
|
||||||
|
pending_dmabufs: Vec::new(),
|
||||||
output,
|
output,
|
||||||
|
seats: FxHashMap::default(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_client(&mut self, client: ClientId, dh: &DisplayHandle) {
|
||||||
|
let seat_data = SeatData::new(dh, client.clone());
|
||||||
|
self.seats.insert(client, seat_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Drop for WaylandState {
|
impl Drop for WaylandState {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -181,15 +147,15 @@ impl ShmHandler for WaylandState {
|
|||||||
}
|
}
|
||||||
impl DmabufHandler for WaylandState {
|
impl DmabufHandler for WaylandState {
|
||||||
fn dmabuf_state(&mut self) -> &mut DmabufState {
|
fn dmabuf_state(&mut self) -> &mut DmabufState {
|
||||||
&mut self.dmabuf_state.0
|
&mut self.dmabuf_state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dmabuf_imported(
|
fn dmabuf_imported(
|
||||||
&mut self,
|
&mut self,
|
||||||
_global: &DmabufGlobal,
|
_global: &DmabufGlobal,
|
||||||
dmabuf: Dmabuf,
|
_dmabuf: Dmabuf,
|
||||||
) -> Result<(), dmabuf::ImportError> {
|
) -> Result<(), dmabuf::ImportError> {
|
||||||
self.dmabuf_tx.send(dmabuf).map_err(|_| ImportError::Failed)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate_dmabuf!(WaylandState);
|
delegate_dmabuf!(WaylandState);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::state::WaylandState;
|
use super::{shaders::PANEL_SHADER_BYTES, state::WaylandState};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{delta::Delta, destroy_queue, registry::Registry},
|
core::{delta::Delta, destroy_queue, registry::Registry},
|
||||||
nodes::drawable::{model::ModelPart, shaders::PANEL_SHADER_BYTES},
|
nodes::drawable::model::ModelPart,
|
||||||
};
|
};
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
@@ -15,13 +15,19 @@ use smithay::{
|
|||||||
},
|
},
|
||||||
desktop::utils::send_frames_surface_tree,
|
desktop::utils::send_frames_surface_tree,
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::wayland_server::{self, protocol::wl_surface::WlSurface, DisplayHandle, Resource},
|
reexports::wayland_server::{
|
||||||
|
self, protocol::wl_surface::WlSurface, Display, DisplayHandle, Resource,
|
||||||
|
},
|
||||||
wayland::compositor::{self, SurfaceData},
|
wayland::compositor::{self, SurfaceData},
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, ffi::c_void, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
ffi::c_void,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
Material, Shader, StereoKitDraw, Tex, TextureAddress, TextureFormat, TextureSample,
|
Material, StereoKitDraw, Tex, TextureAddress, TextureFormat, TextureSample, TextureType,
|
||||||
TextureType, Transparency,
|
Transparency,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static CORE_SURFACES: Registry<CoreSurface> = Registry::new();
|
pub static CORE_SURFACES: Registry<CoreSurface> = Registry::new();
|
||||||
@@ -37,34 +43,34 @@ impl Drop for CoreSurfaceData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CoreSurface {
|
pub struct CoreSurface {
|
||||||
|
display: Weak<Mutex<Display<WaylandState>>>,
|
||||||
pub dh: DisplayHandle,
|
pub dh: DisplayHandle,
|
||||||
pub weak_surface: wayland_server::Weak<WlSurface>,
|
pub weak_surface: wayland_server::Weak<WlSurface>,
|
||||||
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
||||||
sk_tex: OnceCell<Tex>,
|
sk_tex: OnceCell<SendWrapper<Tex>>,
|
||||||
sk_mat: OnceCell<Arc<Material>>,
|
sk_mat: OnceCell<Arc<SendWrapper<Material>>>,
|
||||||
material_offset: Mutex<Delta<u32>>,
|
material_offset: Mutex<Delta<u32>>,
|
||||||
on_mapped: Box<dyn Fn() + Send + Sync>,
|
|
||||||
on_commit: Box<dyn Fn(u32) + Send + Sync>,
|
on_commit: Box<dyn Fn(u32) + Send + Sync>,
|
||||||
pub pending_material_applications: Registry<ModelPart>,
|
pub pending_material_applications: Registry<ModelPart>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreSurface {
|
impl CoreSurface {
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
|
display: &Arc<Mutex<Display<WaylandState>>>,
|
||||||
dh: DisplayHandle,
|
dh: DisplayHandle,
|
||||||
surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
on_mapped: impl Fn() + Send + Sync + 'static,
|
|
||||||
on_commit: impl Fn(u32) + Send + Sync + 'static,
|
on_commit: impl Fn(u32) + Send + Sync + 'static,
|
||||||
) {
|
) {
|
||||||
compositor::with_states(surface, |data| {
|
compositor::with_states(surface, |data| {
|
||||||
data.data_map.insert_if_missing_threadsafe(|| {
|
data.data_map.insert_if_missing_threadsafe(|| {
|
||||||
CORE_SURFACES.add(CoreSurface {
|
CORE_SURFACES.add(CoreSurface {
|
||||||
|
display: Arc::downgrade(display),
|
||||||
dh,
|
dh,
|
||||||
weak_surface: surface.downgrade(),
|
weak_surface: surface.downgrade(),
|
||||||
mapped_data: Mutex::new(None),
|
mapped_data: Mutex::new(None),
|
||||||
sk_tex: OnceCell::new(),
|
sk_tex: OnceCell::new(),
|
||||||
sk_mat: OnceCell::new(),
|
sk_mat: OnceCell::new(),
|
||||||
material_offset: Mutex::new(Delta::new(0)),
|
material_offset: Mutex::new(Delta::new(0)),
|
||||||
on_mapped: Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>,
|
|
||||||
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
|
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
|
||||||
pending_material_applications: Registry::new(),
|
pending_material_applications: Registry::new(),
|
||||||
})
|
})
|
||||||
@@ -83,21 +89,17 @@ impl CoreSurface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&self, sk: &impl StereoKitDraw, renderer: &mut GlesRenderer) {
|
pub fn process(&self, sk: &impl StereoKitDraw, renderer: &mut GlesRenderer) {
|
||||||
let Some(wl_surface) = self.wl_surface() else {return};
|
let Some(wl_surface) = self.wl_surface() else { return };
|
||||||
|
|
||||||
let sk_tex = self
|
let sk_tex = self.sk_tex.get_or_init(|| {
|
||||||
.sk_tex
|
SendWrapper::new(sk.tex_create(TextureType::IMAGE_NO_MIPS, TextureFormat::RGBA32))
|
||||||
.get_or_init(|| sk.tex_create(TextureType::IMAGE_NO_MIPS, TextureFormat::RGBA32));
|
});
|
||||||
self.sk_mat.get_or_init(|| {
|
self.sk_mat.get_or_init(|| {
|
||||||
let shader = sk.shader_create_mem(&PANEL_SHADER_BYTES);
|
let shader = sk.shader_create_mem(&PANEL_SHADER_BYTES).unwrap();
|
||||||
// let _ = renderer.with_context(|c| unsafe {
|
let mat = sk.material_create(&shader);
|
||||||
// shader_inject(c, &mut shader, SIMULA_VERT_STR, SIMULA_FRAG_STR)
|
|
||||||
// });
|
|
||||||
|
|
||||||
let mat = sk.material_create(shader.as_ref().unwrap_or(Shader::UI.as_ref()));
|
|
||||||
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
|
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
|
||||||
sk.material_set_transparency(&mat, Transparency::Blend);
|
sk.material_set_transparency(&mat, Transparency::Blend);
|
||||||
Arc::new(mat)
|
Arc::new(SendWrapper::new(mat))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Let smithay handle buffer management (has to be done here as RendererSurfaceStates is not thread safe)
|
// Let smithay handle buffer management (has to be done here as RendererSurfaceStates is not thread safe)
|
||||||
@@ -119,18 +121,21 @@ impl CoreSurface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut mapped_data = self.mapped_data.lock();
|
let mut mapped_data = self.mapped_data.lock();
|
||||||
let just_mapped = mapped_data.is_none();
|
|
||||||
self.with_states(|data| {
|
self.with_states(|data| {
|
||||||
let Some(renderer_surface_state) = data
|
// let just_mapped = mapped_data.is_none();
|
||||||
|
// if just_mapped {
|
||||||
|
let renderer_surface_state = data
|
||||||
.data_map
|
.data_map
|
||||||
.get::<RendererSurfaceStateUserData>()
|
.get::<RendererSurfaceStateUserData>()
|
||||||
.map(RefCell::borrow) else {return};
|
.unwrap()
|
||||||
let Some(smithay_tex) = renderer_surface_state
|
.borrow();
|
||||||
|
let smithay_tex = renderer_surface_state
|
||||||
.texture::<GlesRenderer>(renderer.id())
|
.texture::<GlesRenderer>(renderer.id())
|
||||||
.cloned() else {return};
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
let Some(sk_tex) = self.sk_tex.get() else {return};
|
let sk_tex = self.sk_tex.get().unwrap();
|
||||||
let Some(sk_mat) = self.sk_mat.get() else {return};
|
let sk_mat = self.sk_mat.get().unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
sk.tex_set_surface(
|
sk.tex_set_surface(
|
||||||
sk_tex.as_ref(),
|
sk_tex.as_ref(),
|
||||||
@@ -149,22 +154,18 @@ impl CoreSurface {
|
|||||||
sk.material_set_queue_offset(sk_mat.as_ref().as_ref(), *material_offset as i32);
|
sk.material_set_queue_offset(sk_mat.as_ref().as_ref(), *material_offset as i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(surface_size) = renderer_surface_state.surface_size() else {return};
|
let surface_size = renderer_surface_state.surface_size().unwrap();
|
||||||
let new_mapped_data = CoreSurfaceData {
|
let new_mapped_data = CoreSurfaceData {
|
||||||
size: Vector2::from([surface_size.w as u32, surface_size.h as u32]),
|
size: Vector2::from([surface_size.w as u32, surface_size.h as u32]),
|
||||||
wl_tex: Some(SendWrapper::new(smithay_tex)),
|
wl_tex: Some(SendWrapper::new(smithay_tex)),
|
||||||
};
|
};
|
||||||
*mapped_data = Some(new_mapped_data);
|
*mapped_data = Some(new_mapped_data);
|
||||||
});
|
});
|
||||||
drop(mapped_data);
|
|
||||||
if just_mapped {
|
|
||||||
(self.on_mapped)();
|
|
||||||
}
|
|
||||||
self.apply_surface_materials();
|
self.apply_surface_materials();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame(&self, sk: &impl StereoKitDraw, output: Output) {
|
pub fn frame(&self, sk: &impl StereoKitDraw, output: Output) {
|
||||||
let Some(wl_surface) = self.wl_surface() else {return};
|
let Some(wl_surface) = self.wl_surface() else { return };
|
||||||
|
|
||||||
send_frames_surface_tree(
|
send_frames_surface_tree(
|
||||||
&wl_surface,
|
&wl_surface,
|
||||||
@@ -179,17 +180,15 @@ impl CoreSurface {
|
|||||||
*self.material_offset.lock().value_mut() = material_offset;
|
*self.material_offset.lock().value_mut() = material_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_material(&self, model_part: &Arc<ModelPart>) {
|
pub fn apply_material(&self, model_node: &Arc<ModelPart>) {
|
||||||
self.pending_material_applications.add_raw(model_part)
|
self.pending_material_applications.add_raw(model_node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_surface_materials(&self) {
|
fn apply_surface_materials(&self) {
|
||||||
if let Some(sk_mat) = self.sk_mat.get() {
|
for model_node in self.pending_material_applications.get_valid_contents() {
|
||||||
for model_node in self.pending_material_applications.get_valid_contents() {
|
model_node.replace_material(self.sk_mat.clone().get().unwrap().clone());
|
||||||
model_node.replace_material(sk_mat.clone());
|
|
||||||
}
|
|
||||||
self.pending_material_applications.clear();
|
|
||||||
}
|
}
|
||||||
|
self.pending_material_applications.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wl_surface(&self) -> Option<WlSurface> {
|
pub fn wl_surface(&self) -> Option<WlSurface> {
|
||||||
@@ -207,6 +206,15 @@ impl CoreSurface {
|
|||||||
pub fn size(&self) -> Option<Vector2<u32>> {
|
pub fn size(&self) -> Option<Vector2<u32>> {
|
||||||
self.mapped_data.lock().as_ref().map(|d| d.size)
|
self.mapped_data.lock().as_ref().map(|d| d.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flush_clients(&self) {
|
||||||
|
self.display
|
||||||
|
.upgrade()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.flush_clients()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Drop for CoreSurface {
|
impl Drop for CoreSurface {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,437 +0,0 @@
|
|||||||
use super::{
|
|
||||||
seat::{KeyboardEvent, PointerEvent, SeatData},
|
|
||||||
state::ClientState,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
nodes::{
|
|
||||||
data::KEYMAPS,
|
|
||||||
drawable::model::ModelPart,
|
|
||||||
items::panel::{Backend, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo},
|
|
||||||
},
|
|
||||||
wayland::surface::CoreSurface,
|
|
||||||
};
|
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use mint::Vector2;
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use smithay::{
|
|
||||||
reexports::{
|
|
||||||
calloop::{EventLoop, LoopSignal},
|
|
||||||
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource},
|
|
||||||
x11rb::protocol::xproto::Window,
|
|
||||||
},
|
|
||||||
utils::{Logical, Rectangle},
|
|
||||||
wayland::compositor,
|
|
||||||
xwayland::{
|
|
||||||
xwm::{Reorder, ResizeEdge, XwmId},
|
|
||||||
X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration};
|
|
||||||
use tokio::sync::oneshot;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
pub static DISPLAY: OnceCell<String> = OnceCell::new();
|
|
||||||
|
|
||||||
pub struct XWaylandState {
|
|
||||||
pub display: u32,
|
|
||||||
event_loop_signal: LoopSignal,
|
|
||||||
}
|
|
||||||
impl XWaylandState {
|
|
||||||
pub fn create(dh: &DisplayHandle) -> Result<Self> {
|
|
||||||
let dh = dh.clone();
|
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let mut event_loop: EventLoop<XWaylandHandler> = EventLoop::try_new()?;
|
|
||||||
let (xwayland, connection) = XWayland::new(&dh);
|
|
||||||
let handle = event_loop.handle();
|
|
||||||
event_loop
|
|
||||||
.handle()
|
|
||||||
.insert_source(connection, {
|
|
||||||
let dh = dh.clone();
|
|
||||||
move |event, _, handler| match event {
|
|
||||||
XWaylandEvent::Ready {
|
|
||||||
connection,
|
|
||||||
client,
|
|
||||||
client_fd: _,
|
|
||||||
display: _,
|
|
||||||
} => {
|
|
||||||
handler.seat = client.get_data::<ClientState>().map(|s| s.seat.clone());
|
|
||||||
handler.wm =
|
|
||||||
X11Wm::start_wm(handle.clone(), dh.clone(), connection, client)
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
XWaylandEvent::Exited => (),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_err(|e| e.error)?;
|
|
||||||
|
|
||||||
let display = xwayland.start(
|
|
||||||
event_loop.handle(),
|
|
||||||
None,
|
|
||||||
empty::<(&OsStr, &OsStr)>(),
|
|
||||||
true,
|
|
||||||
|_| (),
|
|
||||||
)?;
|
|
||||||
let _ = tx.send(XWaylandState {
|
|
||||||
display,
|
|
||||||
event_loop_signal: event_loop.get_signal(),
|
|
||||||
});
|
|
||||||
let mut handler = XWaylandHandler {
|
|
||||||
wayland_display_handle: dh,
|
|
||||||
wm: None,
|
|
||||||
seat: None,
|
|
||||||
};
|
|
||||||
event_loop.run(Duration::from_millis(100), &mut handler, |_| ())
|
|
||||||
});
|
|
||||||
|
|
||||||
let state = rx.blocking_recv()?;
|
|
||||||
let _ = DISPLAY.set(format!(":{}", state.display));
|
|
||||||
|
|
||||||
Ok(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for XWaylandState {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.event_loop_signal.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct XWaylandHandler {
|
|
||||||
wayland_display_handle: DisplayHandle,
|
|
||||||
wm: Option<X11Wm>,
|
|
||||||
seat: Option<Arc<SeatData>>,
|
|
||||||
}
|
|
||||||
impl XWaylandHandler {
|
|
||||||
fn panel_item(&self, window: &X11Surface) -> Option<Arc<PanelItem<X11Backend>>> {
|
|
||||||
compositor::with_states(&window.wl_surface()?, |s| {
|
|
||||||
s.data_map.get::<Arc<PanelItem<X11Backend>>>().cloned()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XwmHandler for XWaylandHandler {
|
|
||||||
fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm {
|
|
||||||
self.wm.as_mut().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "New X window");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "New X override redirect window");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_window_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "X map window request");
|
|
||||||
window.set_mapped(true).unwrap();
|
|
||||||
}
|
|
||||||
fn map_window_notify(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "X map window notify");
|
|
||||||
|
|
||||||
let _ = window.set_maximized(true);
|
|
||||||
|
|
||||||
let dh = self.wayland_display_handle.clone();
|
|
||||||
let seat = self.seat.clone().unwrap();
|
|
||||||
CoreSurface::add_to(
|
|
||||||
self.wayland_display_handle.clone(),
|
|
||||||
&window.wl_surface().unwrap(),
|
|
||||||
{
|
|
||||||
let window = window.clone();
|
|
||||||
move || {
|
|
||||||
let Some(wl_surface) = window.wl_surface() else {return};
|
|
||||||
let seat = seat.clone();
|
|
||||||
window.user_data().insert_if_missing_threadsafe(|| {
|
|
||||||
let panel_item = PanelItem::create(
|
|
||||||
Box::new(X11Backend {
|
|
||||||
toplevel_parent: None,
|
|
||||||
toplevel: window.clone(),
|
|
||||||
seat,
|
|
||||||
_pointer_grab: Mutex::new(None),
|
|
||||||
_keyboard_grab: Mutex::new(None),
|
|
||||||
}),
|
|
||||||
wl_surface
|
|
||||||
.client()
|
|
||||||
.and_then(|c| c.get_credentials(&dh).ok())
|
|
||||||
.map(|c| c.pid),
|
|
||||||
);
|
|
||||||
panel_item
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move |_| {
|
|
||||||
let Some(panel_item) = window.user_data().get::<Arc<PanelItem<X11Backend>>>() else {return};
|
|
||||||
panel_item.toplevel_size_changed(
|
|
||||||
[
|
|
||||||
window.geometry().size.w as u32,
|
|
||||||
window.geometry().size.h as u32,
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "X map override redirect window");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "Unmap X window");
|
|
||||||
}
|
|
||||||
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
debug!(?window, "Destroy X window");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_request(
|
|
||||||
&mut self,
|
|
||||||
_xwm: XwmId,
|
|
||||||
window: X11Surface,
|
|
||||||
x: Option<i32>,
|
|
||||||
y: Option<i32>,
|
|
||||||
w: Option<u32>,
|
|
||||||
h: Option<u32>,
|
|
||||||
reorder: Option<Reorder>,
|
|
||||||
) {
|
|
||||||
debug!(?window, x, y, w, h, ?reorder, "Configure X window");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_notify(
|
|
||||||
&mut self,
|
|
||||||
_xwm: XwmId,
|
|
||||||
window: X11Surface,
|
|
||||||
geometry: Rectangle<i32, Logical>,
|
|
||||||
above: Option<Window>,
|
|
||||||
) {
|
|
||||||
debug!(?window, ?geometry, above, "Configure X window");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) {
|
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
|
||||||
debug!(?window, button, "X window requests move");
|
|
||||||
panel_item.toplevel_move_request();
|
|
||||||
}
|
|
||||||
fn resize_request(
|
|
||||||
&mut self,
|
|
||||||
_xwm: XwmId,
|
|
||||||
window: X11Surface,
|
|
||||||
button: u32,
|
|
||||||
resize_edge: ResizeEdge,
|
|
||||||
) {
|
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
|
||||||
debug!(?window, button, ?resize_edge, "X window requests resize");
|
|
||||||
let (up, down, left, right) = match resize_edge {
|
|
||||||
ResizeEdge::Top => (true, false, false, false),
|
|
||||||
ResizeEdge::Bottom => (false, true, false, false),
|
|
||||||
ResizeEdge::Left => (false, false, true, false),
|
|
||||||
ResizeEdge::TopLeft => (true, false, true, false),
|
|
||||||
ResizeEdge::BottomLeft => (false, true, true, false),
|
|
||||||
ResizeEdge::Right => (false, false, false, true),
|
|
||||||
ResizeEdge::TopRight => (true, false, false, true),
|
|
||||||
ResizeEdge::BottomRight => (false, true, false, true),
|
|
||||||
// _ => (false, false, false, false),
|
|
||||||
};
|
|
||||||
panel_item.toplevel_resize_request(up, down, left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
let _ = window.set_fullscreen(true);
|
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
|
||||||
panel_item.toplevel_fullscreen_active(true);
|
|
||||||
}
|
|
||||||
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
|
||||||
let _ = window.set_fullscreen(false);
|
|
||||||
let Some(panel_item) = self.panel_item(&window) else {return};
|
|
||||||
panel_item.toplevel_fullscreen_active(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct X11Backend {
|
|
||||||
pub toplevel_parent: Option<X11Surface>,
|
|
||||||
pub toplevel: X11Surface,
|
|
||||||
pub seat: Arc<SeatData>,
|
|
||||||
_pointer_grab: Mutex<Option<SurfaceID>>,
|
|
||||||
_keyboard_grab: Mutex<Option<SurfaceID>>,
|
|
||||||
}
|
|
||||||
impl X11Backend {
|
|
||||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
|
||||||
match id {
|
|
||||||
SurfaceID::Cursor => None,
|
|
||||||
SurfaceID::Toplevel => self.toplevel.wl_surface(),
|
|
||||||
SurfaceID::Child(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn flush_client(&self) {
|
|
||||||
// let Some(client) = self.toplevel.wl_surface().and_then(|s| s.client()) else {return};
|
|
||||||
// if let Some(client_state) = client.get_data::<ClientState>() {
|
|
||||||
// client_state.flush();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
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> {
|
|
||||||
Ok(PanelItemInitData {
|
|
||||||
cursor: None,
|
|
||||||
toplevel: ToplevelInfo {
|
|
||||||
parent: None,
|
|
||||||
title: Some(self.toplevel.title()),
|
|
||||||
app_id: Some(self.toplevel.instance()),
|
|
||||||
size: [
|
|
||||||
self.toplevel.geometry().size.w as u32,
|
|
||||||
self.toplevel.geometry().size.h as u32,
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
min_size: self
|
|
||||||
.toplevel
|
|
||||||
.min_size()
|
|
||||||
.map(|s| [s.w as u32, s.h as u32].into()),
|
|
||||||
max_size: self
|
|
||||||
.toplevel
|
|
||||||
.max_size()
|
|
||||||
.map(|s| [s.w as u32, s.h as u32].into()),
|
|
||||||
logical_rectangle: Geometry {
|
|
||||||
origin: [0, 0].into(),
|
|
||||||
size: [
|
|
||||||
self.toplevel.geometry().size.w as u32,
|
|
||||||
self.toplevel.geometry().size.h as u32,
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: FxHashMap::default(),
|
|
||||||
pointer_grab: self._pointer_grab.lock().clone(),
|
|
||||||
keyboard_grab: self._keyboard_grab.lock().clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn close_toplevel(&self) {}
|
|
||||||
|
|
||||||
fn auto_size_toplevel(&self) {
|
|
||||||
let _ = self.toplevel.configure(None);
|
|
||||||
}
|
|
||||||
fn set_toplevel_size(&self, size: Vector2<u32>) {
|
|
||||||
let _ = self.toplevel.configure(Some(Rectangle {
|
|
||||||
loc: self.toplevel.geometry().loc,
|
|
||||||
size: (size.x as i32, size.y as i32).into(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
fn set_toplevel_focused_visuals(&self, focused: bool) {
|
|
||||||
let _ = self.toplevel.set_activated(focused);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return};
|
|
||||||
|
|
||||||
core_surface.apply_material(model_part);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
|
||||||
self.seat
|
|
||||||
.pointer_event(&surface, PointerEvent::Motion(position));
|
|
||||||
}
|
|
||||||
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
|
||||||
self.seat.pointer_event(
|
|
||||||
&surface,
|
|
||||||
PointerEvent::Button {
|
|
||||||
button,
|
|
||||||
state: if pressed { 1 } else { 0 },
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
fn pointer_scroll(
|
|
||||||
&self,
|
|
||||||
surface: &SurfaceID,
|
|
||||||
scroll_distance: Option<Vector2<f32>>,
|
|
||||||
scroll_steps: Option<Vector2<f32>>,
|
|
||||||
) {
|
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
|
||||||
self.seat.pointer_event(
|
|
||||||
&surface,
|
|
||||||
PointerEvent::Scroll {
|
|
||||||
axis_continuous: scroll_distance,
|
|
||||||
axis_discrete: scroll_steps,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) {
|
|
||||||
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
|
||||||
let keymaps = KEYMAPS.lock();
|
|
||||||
let Some(keymap) = keymaps.get(keymap_id).cloned() else {return};
|
|
||||||
if self.seat.set_keymap(keymap, vec![surface.clone()]).is_err() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for key in keys {
|
|
||||||
self.seat.keyboard_event(
|
|
||||||
&surface,
|
|
||||||
KeyboardEvent::Key {
|
|
||||||
key: key.abs() as u32,
|
|
||||||
state: if key < 0 { 1 } else { 0 },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user