Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
807928a8d3 | ||
|
|
f74c891907 | ||
|
|
2dc0fdadfd | ||
|
|
8e317a8e08 | ||
|
|
c41179a437 | ||
|
|
43910cce78 |
119
Cargo.lock
generated
119
Cargo.lock
generated
@@ -602,9 +602,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "drm"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58eefd79f5173683872c0c82d0f05c2dc3c583d631259f60bb7a323756b7ff2"
|
||||
checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bytemuck",
|
||||
@@ -615,9 +615,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "drm-ffi"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "220dd8c12ebf2b0cbaffa19e00de02f5f090d363fb900f16ea012c077eea1174"
|
||||
checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6"
|
||||
dependencies = [
|
||||
"drm-sys",
|
||||
"rustix",
|
||||
@@ -631,9 +631,9 @@ checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
||||
|
||||
[[package]]
|
||||
name = "drm-sys"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5115283ec60c99da8a9e5dc3c55f27680211e974c948cb6f3b51f0373190503b"
|
||||
checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.6.1",
|
||||
@@ -819,12 +819,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "0.3.0"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177"
|
||||
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1648,7 +1648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"toml_edit",
|
||||
"toml_edit 0.19.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1737,9 +1737,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -1974,6 +1974,15 @@ dependencies = [
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
@@ -2016,7 +2025,7 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/smithay/smithay.git#5c688b89fc97ade8c60c45d4a319311b7ec5292f"
|
||||
source = "git+https://github.com/smithay/smithay.git#91e61f13501f21d66803efac947bfafed43080c5"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags 2.4.0",
|
||||
@@ -2117,7 +2126,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "stardust-xr-server"
|
||||
version = "0.44.0"
|
||||
version = "0.44.1"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
@@ -2149,6 +2158,7 @@ dependencies = [
|
||||
"stardust-xr-server-codegen",
|
||||
"stereokit",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-tracy",
|
||||
@@ -2347,10 +2357,25 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
name = "toml"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
@@ -2360,7 +2385,20 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
"winnow 0.5.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2575,13 +2613,13 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4"
|
||||
checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"nix 0.26.4",
|
||||
"rustix",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
@@ -2627,9 +2665,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.0"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c"
|
||||
checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
@@ -2689,15 +2727,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-wsapoll"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -2789,26 +2818,30 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.12.0"
|
||||
name = "winnow"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a"
|
||||
checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"nix 0.26.4",
|
||||
"winapi",
|
||||
"winapi-wsapoll",
|
||||
"rustix",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc"
|
||||
dependencies = [
|
||||
"nix 0.26.4",
|
||||
]
|
||||
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
|
||||
|
||||
[[package]]
|
||||
name = "xkbcommon"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
edition = "2021"
|
||||
rust-version = "1.75"
|
||||
name = "stardust-xr-server"
|
||||
version = "0.44.0"
|
||||
version = "0.44.1"
|
||||
authors = ["Nova King <technobaboo@proton.me>"]
|
||||
description = "Stardust XR reference display server"
|
||||
license = "GPLv2"
|
||||
@@ -68,10 +68,11 @@ ctrlc = "3.4.1"
|
||||
libc = "0.2.148"
|
||||
input-event-codes = "5.16.8"
|
||||
nix = "0.27.1"
|
||||
wayland-scanner = "0.31.0"
|
||||
wayland-backend = "0.3.2"
|
||||
wayland-scanner = "0.31.1"
|
||||
wayland-backend = "0.3.3"
|
||||
cluFlock = "1.2.7"
|
||||
fxtypemap = "0.2.0"
|
||||
toml = "0.8.10"
|
||||
|
||||
[dependencies.smithay]
|
||||
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||
|
||||
162
flake.lock
generated
162
flake.lock
generated
@@ -9,11 +9,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1678775037,
|
||||
"narHash": "sha256-chx0tWnXKpcayPkPY3Qh+2hNwptvX8XE3o+fYZ+GNzg=",
|
||||
"lastModified": 1700115779,
|
||||
"narHash": "sha256-oajhxEBg+16/KH74CaygAQ6b5KUHS7DwBoL9ecD9qeI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "ee59e1c769657b1e27e608f8b981fa8f6b715583",
|
||||
"rev": "4378e7e5f5bdef438eee5ce967f37593b9b5cd16",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -27,11 +27,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1690933134,
|
||||
"narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=",
|
||||
"lastModified": 1709336216,
|
||||
"narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb",
|
||||
"rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -42,14 +42,17 @@
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib_2"
|
||||
"nixpkgs-lib": [
|
||||
"hercules-ci-effects",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688466019,
|
||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
||||
"lastModified": 1701473968,
|
||||
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
||||
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -57,39 +60,17 @@
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"flake-parts_3": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"hercules-ci-effects",
|
||||
"hercules-ci-agent",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688466019,
|
||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flatland": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694190588,
|
||||
"narHash": "sha256-tcvp09A54AbXkkXS/P6Ed5TUWzMEuQ9h/fnfLz7gnJc=",
|
||||
"lastModified": 1709774755,
|
||||
"narHash": "sha256-6tSG4G+SB+l71101XNyEHN/J8pSuJvbNPhKgzqP5sVU=",
|
||||
"owner": "StardustXR",
|
||||
"repo": "flatland",
|
||||
"rev": "3867067452761959a95497d6589f9336b347270c",
|
||||
"rev": "fef8c317928a1d1798d8cee9af94d219a7c09e8c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -98,53 +79,17 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"haskell-flake": {
|
||||
"locked": {
|
||||
"lastModified": 1684780604,
|
||||
"narHash": "sha256-2uMZsewmRn7rRtAnnQNw1lj0uZBMh4m6Cs/7dV5YF08=",
|
||||
"owner": "srid",
|
||||
"repo": "haskell-flake",
|
||||
"rev": "74210fa80a49f1b6f67223debdbf1494596ff9f2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "srid",
|
||||
"ref": "0.3.0",
|
||||
"repo": "haskell-flake",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hercules-ci-agent": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_3",
|
||||
"haskell-flake": "haskell-flake",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688568579,
|
||||
"narHash": "sha256-ON0M56wtY/TIIGPkXDlJboAmuYwc73Hi8X9iJGtxOhM=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "hercules-ci-agent",
|
||||
"rev": "367dd8cd649b57009a6502e878005a1e54ad78c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "hercules-ci-agent",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"hercules-ci-effects": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
"hercules-ci-agent": "hercules-ci-agent",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1689397210,
|
||||
"narHash": "sha256-fVxZnqxMbsDkB4GzGAs/B41K0wt/e+B/fLxmTFF/S20=",
|
||||
"lastModified": 1708547820,
|
||||
"narHash": "sha256-xU/KC1PWqq5zL9dQ9wYhcdgxAwdeF/dJCLPH3PNZEBg=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "hercules-ci-effects",
|
||||
"rev": "0a63bfa3f00a3775ea3a6722b247880f1ffe91ce",
|
||||
"rev": "0ca27bd58e4d5be3135a4bef66b582e57abe8f4a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -155,16 +100,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678703398,
|
||||
"narHash": "sha256-Y1mW3dBsoWLHpYm+UIHb5VZ7rx024NNHaF16oZBx++o=",
|
||||
"lastModified": 1699781429,
|
||||
"narHash": "sha256-UYefjidASiLORAjIvVsUHG6WBtRhM67kTjEY4XfZOFs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "67f26c1cfc5d5783628231e776a81c1ade623e0b",
|
||||
"rev": "e44462d6021bfe23dfb24b775cc7c390844f773d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-22.11",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -172,29 +117,11 @@
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1690881714,
|
||||
"narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=",
|
||||
"lastModified": 1709237383,
|
||||
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9e1960bc196baf6881340d53dccb203a951745a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib_2": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1688049487,
|
||||
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
|
||||
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -207,11 +134,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1688322751,
|
||||
"narHash": "sha256-eW62dC5f33oKZL7VWlomttbUnOTHrAbte9yNUNW8rbk=",
|
||||
"lastModified": 1703637592,
|
||||
"narHash": "sha256-8MXjxU0RfFfzl57Zy3OfXCITS0qWDNLzlBAdwxGZwfY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0fbe93c5a7cac99f90b60bdf5f149383daaa615f",
|
||||
"rev": "cfc3698c31b1fb9cdcf10f36c9643460264d0ca8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -223,26 +150,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1689393711,
|
||||
"narHash": "sha256-l3gyTPy/qWLDk1CY1EgYwlsxcGxN2aVd7MlCzQa69rk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "27fcd46fa18df36d270174246e7bd8f1787129ff",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1692734709,
|
||||
"narHash": "sha256-SCFnyHCyYjwEmgUsHDDuU0TsbVMKeU1vwkR+r7uS2Rg=",
|
||||
"lastModified": 1709479366,
|
||||
"narHash": "sha256-n6F0n8UV6lnTZbYPl1A9q1BS0p4hduAv1mGAP17CVd0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b85ed9dcbf187b909ef7964774f8847d554fab3b",
|
||||
"rev": "b8697e57f10292a6165a20f03d2f42920dfaf973",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -257,17 +169,17 @@
|
||||
"flake-parts": "flake-parts",
|
||||
"flatland": "flatland",
|
||||
"hercules-ci-effects": "hercules-ci-effects",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1678695923,
|
||||
"narHash": "sha256-rDhiiU8P6sf6mgj5IKgCuTRN9uYeqWr6xl4XLkKnMWg=",
|
||||
"lastModified": 1700077026,
|
||||
"narHash": "sha256-Vf7ykubXsriSjBbeYAm8bzBIvSOYVUmRiCQ3iLL/E+U=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "95497533524537b1cc7a2870ce94b0b14503be8b",
|
||||
"rev": "58de0b130a763f3a2d373f508ac0c18a8e7d0acd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -16,7 +16,7 @@ use stardust_xr::{
|
||||
messenger::{self, MessageSenderHandle},
|
||||
schemas::flex::serialize,
|
||||
};
|
||||
use std::{fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
||||
use std::{fmt::Debug, fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
||||
use tokio::{net::UnixStream, task::JoinHandle};
|
||||
use tracing::info;
|
||||
|
||||
@@ -215,6 +215,19 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Debug for Client {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Client")
|
||||
.field("pid", &self.pid)
|
||||
.field("exe", &self.exe)
|
||||
.field("dispatch_join_handle", &self.dispatch_join_handle)
|
||||
.field("flush_join_handle", &self.flush_join_handle)
|
||||
.field("disconnect_status", &self.disconnect_status)
|
||||
.field("base_resource_prefixes", &self.base_resource_prefixes)
|
||||
.field("state", &self.state)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
impl Drop for Client {
|
||||
fn drop(&mut self) {
|
||||
info!(
|
||||
|
||||
@@ -4,7 +4,11 @@ use glam::Mat4;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{io::Write, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientState>>> = Default::default();
|
||||
@@ -29,7 +33,7 @@ impl LaunchInfo {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ClientState {
|
||||
pub launch_info: Option<LaunchInfo>,
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub data: Vec<u8>,
|
||||
pub root: Mat4,
|
||||
pub spatial_anchors: FxHashMap<String, Mat4>,
|
||||
}
|
||||
@@ -37,7 +41,7 @@ impl ClientState {
|
||||
pub fn from_deserialized(client: &Client, state: ClientStateInternal) -> Self {
|
||||
ClientState {
|
||||
launch_info: LaunchInfo::from_client(client),
|
||||
data: state.data,
|
||||
data: state.data.unwrap_or_default(),
|
||||
root: Self::spatial_transform(client, &state.root.unwrap_or_default())
|
||||
.unwrap_or_default(),
|
||||
spatial_anchors: state
|
||||
@@ -58,20 +62,29 @@ impl ClientState {
|
||||
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 from_file(file: &Path) -> Option<Self> {
|
||||
let file_string = std::fs::read_to_string(file).ok()?;
|
||||
toml::from_str(&file_string).ok()
|
||||
}
|
||||
pub fn to_file(self, directory: &Path) {
|
||||
let app_name = self
|
||||
.launch_info
|
||||
.as_ref()
|
||||
.map(|l| l.cmdline.get(0).unwrap().split('/').last().unwrap())
|
||||
.unwrap_or("unknown");
|
||||
let state_file_path = directory
|
||||
.join(format!("{app_name}-{}", nanoid::nanoid!()))
|
||||
.with_extension("toml");
|
||||
|
||||
std::fs::write(state_file_path, toml::to_string(&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(),
|
||||
data: Some(self.data.clone()),
|
||||
root: Some("/".to_string()),
|
||||
spatial_anchors: self
|
||||
.spatial_anchors
|
||||
@@ -88,12 +101,22 @@ impl ClientState {
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
pub fn launch_command(self) -> Option<Command> {
|
||||
let launch_info = self.launch_info.as_ref()?;
|
||||
let mut cmdline = launch_info.cmdline.iter();
|
||||
let mut command = Command::new(cmdline.next()?);
|
||||
command.args(cmdline);
|
||||
command.current_dir(&launch_info.cwd);
|
||||
command.envs(launch_info.env.iter());
|
||||
command.env("STARDUST_STARTUP_TOKEN", self.token());
|
||||
Some(command)
|
||||
}
|
||||
}
|
||||
impl Default for ClientState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
launch_info: None,
|
||||
data: None,
|
||||
data: Default::default(),
|
||||
root: Mat4::IDENTITY,
|
||||
spatial_anchors: Default::default(),
|
||||
}
|
||||
|
||||
208
src/main.rs
208
src/main.rs
@@ -5,7 +5,6 @@ mod objects;
|
||||
mod wayland;
|
||||
|
||||
use crate::core::client::CLIENTS;
|
||||
use crate::core::client_state::ClientState;
|
||||
use crate::core::destroy_queue;
|
||||
use crate::nodes::items::camera;
|
||||
use crate::nodes::{audio, drawable, hmd, input};
|
||||
@@ -18,12 +17,13 @@ use crate::wayland::X_DISPLAY;
|
||||
|
||||
use self::core::eventloop::EventLoop;
|
||||
use clap::Parser;
|
||||
use core::client_state::ClientState;
|
||||
use directories::ProjectDirs;
|
||||
use once_cell::sync::OnceCell;
|
||||
use stardust_xr::server;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use stereokit::{
|
||||
@@ -56,6 +56,10 @@ struct CliArgs {
|
||||
/// Run a script when ready for clients to connect. If this is not set the script at $HOME/.config/stardust/startup will be ran if it exists.
|
||||
#[clap(id = "PATH", short = 'e', long = "execute-startup-script", action)]
|
||||
startup_script: Option<PathBuf>,
|
||||
|
||||
/// Restore the session with the given ID (or `latest`), ignoring the startup script. Sessions are stored in directories at `~/.local/state/stardust/`.
|
||||
#[clap(id = "SESSION_ID", long = "restore", action)]
|
||||
restore: Option<String>,
|
||||
}
|
||||
|
||||
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
|
||||
@@ -197,7 +201,10 @@ fn main() {
|
||||
let (info_sender, info_receiver) = oneshot::channel::<EventLoopInfo>();
|
||||
let event_thread = std::thread::Builder::new()
|
||||
.name("event_loop".to_owned())
|
||||
.spawn(move || event_loop(info_sender))
|
||||
.spawn({
|
||||
let project_dirs = project_dirs.clone();
|
||||
move || event_loop(info_sender, project_dirs.clone())
|
||||
})
|
||||
.unwrap();
|
||||
let event_loop_info = info_receiver.blocking_recv().unwrap();
|
||||
let _tokio_handle = event_loop_info.tokio_handle.enter();
|
||||
@@ -206,56 +213,18 @@ fn main() {
|
||||
let mut wayland = wayland::Wayland::new().expect("Could not initialize wayland");
|
||||
info!("Stardust ready!");
|
||||
|
||||
let mut startup_child = (|| {
|
||||
let project_dirs = project_dirs.as_ref()?;
|
||||
let startup_script_path = cli_args
|
||||
.startup_script
|
||||
.clone()
|
||||
.and_then(|p| p.canonicalize().ok())
|
||||
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
||||
let mut startup_command = Command::new("bash");
|
||||
startup_command.arg(startup_script_path);
|
||||
startup_command.arg("&");
|
||||
|
||||
startup_command.stdin(Stdio::null());
|
||||
startup_command.stdout(Stdio::null());
|
||||
startup_command.stderr(Stdio::null());
|
||||
startup_command.env(
|
||||
"FLAT_WAYLAND_DISPLAY",
|
||||
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
||||
);
|
||||
startup_command.env(
|
||||
"STARDUST_INSTANCE",
|
||||
event_loop_info
|
||||
.socket_path
|
||||
.file_name()
|
||||
.expect("Stardust socket path not found"),
|
||||
);
|
||||
#[cfg(feature = "wayland")]
|
||||
{
|
||||
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
||||
startup_command.env("WAYLAND_DISPLAY", &wayland_socket);
|
||||
}
|
||||
startup_command.env(
|
||||
"DISPLAY",
|
||||
format!(":{}", X_DISPLAY.get().cloned().unwrap_or_default()),
|
||||
);
|
||||
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 startup_children = project_dirs
|
||||
.as_ref()
|
||||
.map(|project_dirs| {
|
||||
launch_start(
|
||||
&cli_args,
|
||||
project_dirs,
|
||||
&event_loop_info,
|
||||
#[cfg(feature = "wayland")]
|
||||
&wayland,
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut last_frame_delta = Duration::ZERO;
|
||||
let mut sleep_duration = Duration::ZERO;
|
||||
@@ -306,10 +275,6 @@ fn main() {
|
||||
},
|
||||
|_sk| {
|
||||
info!("Cleanly shut down StereoKit");
|
||||
|
||||
if let Some(mut startup_child) = startup_child.take() {
|
||||
let _ = startup_child.kill();
|
||||
}
|
||||
},
|
||||
)
|
||||
});
|
||||
@@ -322,6 +287,9 @@ fn main() {
|
||||
.join()
|
||||
.expect("Failed to cleanly shut down event loop")
|
||||
.unwrap();
|
||||
for mut startup_child in startup_children.drain(..) {
|
||||
let _ = startup_child.kill();
|
||||
}
|
||||
|
||||
info!("Cleanly shut down Stardust");
|
||||
}
|
||||
@@ -349,9 +317,12 @@ fn adaptive_sleep(
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
// #[tokio::main(flavor = "current_thread")]
|
||||
async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::eyre::Result<()> {
|
||||
// #[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn event_loop(
|
||||
info_sender: oneshot::Sender<EventLoopInfo>,
|
||||
project_dirs: Option<ProjectDirs>,
|
||||
) -> color_eyre::eyre::Result<()> {
|
||||
let socket_path =
|
||||
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
||||
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
||||
@@ -368,7 +339,9 @@ async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::
|
||||
|
||||
STOP_NOTIFIER.notified().await;
|
||||
println!("Stopping...");
|
||||
save_clients().await;
|
||||
if let Some(project_dirs) = project_dirs {
|
||||
save_session(&project_dirs).await;
|
||||
}
|
||||
|
||||
info!("Cleanly shut down event loop");
|
||||
|
||||
@@ -379,16 +352,121 @@ async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_clients() {
|
||||
fn launch_start(
|
||||
cli_args: &CliArgs,
|
||||
project_dirs: &ProjectDirs,
|
||||
event_loop_info: &EventLoopInfo,
|
||||
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||
) -> Vec<Child> {
|
||||
if let Some(session_id) = &cli_args.restore {
|
||||
let session_dir = project_dirs.state_dir().unwrap().join(session_id);
|
||||
return restore_session(&session_dir, event_loop_info, wayland);
|
||||
}
|
||||
let startup_script_path = cli_args
|
||||
.startup_script
|
||||
.clone()
|
||||
.and_then(|p| p.canonicalize().ok())
|
||||
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
||||
run_script(&startup_script_path, event_loop_info, wayland)
|
||||
}
|
||||
|
||||
fn restore_session(
|
||||
session_dir: &Path,
|
||||
event_loop_info: &EventLoopInfo,
|
||||
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||
) -> Vec<Child> {
|
||||
let Ok(clients) = session_dir.read_dir() else {
|
||||
return Vec::new();
|
||||
};
|
||||
clients
|
||||
.filter_map(Result::ok)
|
||||
.filter_map(|c| ClientState::from_file(&c.path()))
|
||||
.filter_map(ClientState::launch_command)
|
||||
.filter_map(|startup_command| {
|
||||
run_client(
|
||||
startup_command,
|
||||
event_loop_info,
|
||||
#[cfg(feature = "wayland")]
|
||||
wayland,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn run_script(
|
||||
script_path: &Path,
|
||||
event_loop_info: &EventLoopInfo,
|
||||
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||
) -> Vec<Child> {
|
||||
let _ = std::fs::set_permissions(script_path, std::fs::Permissions::from_mode(0o755));
|
||||
let startup_command = Command::new(script_path);
|
||||
run_client(
|
||||
startup_command,
|
||||
event_loop_info,
|
||||
#[cfg(feature = "wayland")]
|
||||
wayland,
|
||||
)
|
||||
.map(|c| vec![c])
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn run_client(
|
||||
mut command: Command,
|
||||
event_loop_info: &EventLoopInfo,
|
||||
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||
) -> Option<Child> {
|
||||
command.stdin(Stdio::null());
|
||||
command.stdout(Stdio::null());
|
||||
command.stderr(Stdio::null());
|
||||
command.env(
|
||||
"FLAT_WAYLAND_DISPLAY",
|
||||
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
||||
);
|
||||
command.env(
|
||||
"STARDUST_INSTANCE",
|
||||
event_loop_info
|
||||
.socket_path
|
||||
.file_name()
|
||||
.expect("Stardust socket path not found"),
|
||||
);
|
||||
#[cfg(feature = "wayland")]
|
||||
{
|
||||
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
||||
command.env("WAYLAND_DISPLAY", &wayland_socket);
|
||||
}
|
||||
command.env(
|
||||
"DISPLAY",
|
||||
format!(":{}", X_DISPLAY.get().cloned().unwrap_or_default()),
|
||||
);
|
||||
command.env("GDK_BACKEND", "wayland");
|
||||
command.env("QT_QPA_PLATFORM", "wayland");
|
||||
command.env("MOZ_ENABLE_WAYLAND", "1");
|
||||
command.env("CLUTTER_BACKEND", "wayland");
|
||||
command.env("SDL_VIDEODRIVER", "wayland");
|
||||
}
|
||||
let child = command.spawn().ok()?;
|
||||
Some(child)
|
||||
}
|
||||
|
||||
async fn save_session(project_dirs: &ProjectDirs) {
|
||||
let session_id = nanoid::nanoid!();
|
||||
let state_dir = project_dirs.state_dir().unwrap();
|
||||
let session_dir = state_dir.join(&session_id);
|
||||
std::fs::create_dir_all(&session_dir).unwrap();
|
||||
let _ = std::fs::remove_dir_all(state_dir.join("latest"));
|
||||
std::os::unix::fs::symlink(&session_dir, state_dir.join("latest")).unwrap();
|
||||
|
||||
let local_set = LocalSet::new();
|
||||
for client in CLIENTS.get_vec() {
|
||||
let session_dir = session_dir.clone();
|
||||
local_set.spawn_local(async move {
|
||||
tokio::select! {
|
||||
biased;
|
||||
s = client.save_state() => {s.map(ClientState::to_file);},
|
||||
s = client.save_state() => {s.map(|s| s.to_file(&session_dir));},
|
||||
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
local_set.await;
|
||||
println!("Session ID for restore is {session_id}");
|
||||
}
|
||||
|
||||
@@ -170,10 +170,16 @@ impl InputMethod {
|
||||
.add(self as *const InputMethod as usize, &method_alias);
|
||||
}
|
||||
|
||||
fn compare_distance(&self, to: &Field) -> f32 {
|
||||
self.specialization
|
||||
fn compare_distance(&self, to: &InputHandler) -> f32 {
|
||||
let distance = self
|
||||
.specialization
|
||||
.lock()
|
||||
.compare_distance(&self.spatial, to)
|
||||
.compare_distance(&self.spatial, &to.field);
|
||||
if self.captures.contains(to) {
|
||||
distance * 0.5
|
||||
} else {
|
||||
distance
|
||||
}
|
||||
}
|
||||
fn true_distance(&self, to: &Field) -> f32 {
|
||||
self.specialization.lock().true_distance(&self.spatial, to)
|
||||
@@ -253,7 +259,7 @@ pub struct DistanceLink {
|
||||
impl DistanceLink {
|
||||
fn from(method: Arc<InputMethod>, handler: Arc<InputHandler>) -> Self {
|
||||
DistanceLink {
|
||||
distance: method.compare_distance(&handler.field),
|
||||
distance: method.compare_distance(&handler),
|
||||
method,
|
||||
handler,
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use serde::{
|
||||
};
|
||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, info};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
||||
@@ -220,7 +220,7 @@ pub struct PanelItem<B: Backend + ?Sized> {
|
||||
pub backend: Box<B>,
|
||||
}
|
||||
impl<B: Backend + ?Sized> PanelItem<B> {
|
||||
pub fn create(backend: Box<B>, pid: Option<i32>) -> Arc<PanelItem<B>> {
|
||||
pub fn create(backend: Box<B>, pid: Option<i32>) -> (Arc<Node>, Arc<PanelItem<B>>) {
|
||||
debug!(?pid, "Create panel item");
|
||||
|
||||
let startup_settings = pid
|
||||
@@ -228,9 +228,12 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
||||
.and_then(|env| state(&env));
|
||||
|
||||
let uid = nanoid!();
|
||||
let node = Node::create_parent_name(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
||||
.add_to_scenegraph()
|
||||
.unwrap();
|
||||
let node = Arc::new(Node::create_parent_name(
|
||||
&INTERNAL_CLIENT,
|
||||
"/item/panel/item",
|
||||
&uid,
|
||||
true,
|
||||
));
|
||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
|
||||
if let Some(startup_settings) = &startup_settings {
|
||||
spatial.set_local_transform(startup_settings.root);
|
||||
@@ -266,7 +269,7 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
||||
node.add_local_signal("touch_up", Self::touch_up_flex);
|
||||
node.add_local_signal("reset_touches", Self::reset_touches_flex);
|
||||
|
||||
panel_item
|
||||
(node, panel_item)
|
||||
}
|
||||
pub fn drop_toplevel(&self) {
|
||||
let Some(node) = self.node.upgrade() else {
|
||||
@@ -593,5 +596,6 @@ impl<B: Backend + ?Sized> Backend for PanelItem<B> {
|
||||
impl<B: Backend + ?Sized> Drop for PanelItem<B> {
|
||||
fn drop(&mut self) {
|
||||
// Dropped panel item, basically just a debug breakpoint place
|
||||
info!("Dropped panel item {}", self.uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,10 @@ impl Root {
|
||||
spatial.set_local_transform(transform);
|
||||
}
|
||||
pub async fn save_state(&self) -> Result<ClientStateInternal> {
|
||||
Ok(self
|
||||
.node
|
||||
self.node
|
||||
.execute_remote_method_typed("save_state", (), Vec::new())
|
||||
.await?
|
||||
.0)
|
||||
.await
|
||||
.map(|(m, _)| m)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,8 +117,13 @@ impl Dispatch<wl_drm::WlDrm, (), WaylandState> for WaylandState {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut dma = Dmabuf::builder((width, height), format, DmabufFlags::empty());
|
||||
dma.add_plane(name, 0, offset0 as u32, stride0 as u32, Modifier::Invalid);
|
||||
let mut dma = Dmabuf::builder(
|
||||
(width, height),
|
||||
format,
|
||||
Modifier::Invalid,
|
||||
DmabufFlags::empty(),
|
||||
);
|
||||
dma.add_plane(name, 0, offset0 as u32, stride0 as u32);
|
||||
match dma.build() {
|
||||
Some(dmabuf) => {
|
||||
state.dmabuf_tx.send((dmabuf.clone(), None)).unwrap();
|
||||
|
||||
@@ -6,6 +6,7 @@ mod state;
|
||||
mod surface;
|
||||
// mod xdg_activation;
|
||||
mod drm;
|
||||
mod utils;
|
||||
mod xdg_shell;
|
||||
#[cfg(feature = "xwayland_rootful")]
|
||||
pub mod xwayland_rootful;
|
||||
@@ -30,6 +31,7 @@ use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::egl::EGLContext;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::ImportDma;
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_server::backend::ClientId;
|
||||
use smithay::reexports::wayland_server::DisplayHandle;
|
||||
use smithay::reexports::wayland_server::{Display, ListeningSocket};
|
||||
@@ -97,8 +99,8 @@ pub struct Wayland {
|
||||
pub socket_name: Option<String>,
|
||||
join_handle: JoinHandle<Result<()>>,
|
||||
renderer: GlesRenderer,
|
||||
output: Output,
|
||||
dmabuf_rx: UnboundedReceiver<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||
wayland_state: Arc<Mutex<WaylandState>>,
|
||||
#[cfg(feature = "xwayland_rootful")]
|
||||
pub x_lock: X11Lock,
|
||||
#[cfg(feature = "xwayland_rootless")]
|
||||
@@ -124,6 +126,7 @@ impl Wayland {
|
||||
#[cfg(feature = "xwayland_rootless")]
|
||||
let xwayland_state = XWaylandState::create(&display_handle)?;
|
||||
let wayland_state = WaylandState::new(display_handle, &renderer, dmabuf_tx);
|
||||
let output = wayland_state.lock().output.clone();
|
||||
|
||||
let socket = ListeningSocket::bind_auto("wayland", 0..33)?;
|
||||
let socket_name = socket
|
||||
@@ -137,15 +140,14 @@ impl Wayland {
|
||||
let x_display = start_xwayland(socket.as_raw_fd())?;
|
||||
info!(socket_name, "Wayland active");
|
||||
|
||||
let join_handle = Wayland::start_loop(display.clone(), socket, wayland_state.clone())?;
|
||||
|
||||
let join_handle = Wayland::start_loop(display.clone(), socket, wayland_state)?;
|
||||
Ok(Wayland {
|
||||
display,
|
||||
socket_name,
|
||||
join_handle,
|
||||
renderer,
|
||||
output,
|
||||
dmabuf_rx,
|
||||
wayland_state,
|
||||
#[cfg(feature = "xwayland_rootful")]
|
||||
x_lock: x_display,
|
||||
#[cfg(feature = "xwayland_rootless")]
|
||||
@@ -185,7 +187,7 @@ impl Wayland {
|
||||
e = dispatch_poll_listener.readable() => { // Dispatch
|
||||
let mut guard = e?;
|
||||
debug_span!("Dispatch wayland event").in_scope(|| -> Result<(), color_eyre::Report> {
|
||||
display.dispatch_clients(&mut *state.lock())?;
|
||||
display.dispatch_clients(&mut state.lock())?;
|
||||
display.flush_clients(None);
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -213,10 +215,8 @@ impl Wayland {
|
||||
}
|
||||
|
||||
pub fn frame_event(&self, sk: &impl StereoKitDraw) {
|
||||
let output = self.wayland_state.lock().output.clone();
|
||||
|
||||
for core_surface in CORE_SURFACES.get_valid_contents() {
|
||||
core_surface.frame(sk, output.clone());
|
||||
core_surface.frame(sk, self.output.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ use smithay::{
|
||||
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode,
|
||||
wayland_server::{
|
||||
backend::{ClientData, ClientId, DisconnectReason},
|
||||
protocol::{wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager},
|
||||
protocol::{
|
||||
wl_buffer::WlBuffer, wl_data_device_manager::WlDataDeviceManager,
|
||||
wl_output::WlOutput,
|
||||
},
|
||||
DisplayHandle,
|
||||
},
|
||||
},
|
||||
@@ -29,6 +32,7 @@ use smithay::{
|
||||
dmabuf::{
|
||||
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||
},
|
||||
output::OutputHandler,
|
||||
shell::kde::decoration::KdeDecorationState,
|
||||
shm::{ShmHandler, ShmState},
|
||||
},
|
||||
@@ -201,6 +205,9 @@ impl DmabufHandler for WaylandState {
|
||||
self.dmabuf_tx.send((dmabuf, Some(notifier))).unwrap();
|
||||
}
|
||||
}
|
||||
impl OutputHandler for WaylandState {
|
||||
fn output_bound(&mut self, _output: Output, _wl_output: WlOutput) {}
|
||||
}
|
||||
delegate_dmabuf!(WaylandState);
|
||||
delegate_shm!(WaylandState);
|
||||
delegate_output!(WaylandState);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::state::WaylandState;
|
||||
use super::{state::WaylandState, utils::get_data};
|
||||
use crate::{
|
||||
core::{delta::Delta, destroy_queue, registry::Registry},
|
||||
nodes::drawable::{model::ModelPart, shaders::PANEL_SHADER_BYTES},
|
||||
@@ -77,13 +77,13 @@ impl CoreSurface {
|
||||
}
|
||||
|
||||
pub fn from_wl_surface(surf: &WlSurface) -> Option<Arc<CoreSurface>> {
|
||||
compositor::with_states(surf, |data| {
|
||||
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
||||
})
|
||||
get_data(surf)
|
||||
}
|
||||
|
||||
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
|
||||
.sk_tex
|
||||
@@ -124,13 +124,23 @@ impl CoreSurface {
|
||||
let Some(renderer_surface_state) = data
|
||||
.data_map
|
||||
.get::<RendererSurfaceStateUserData>()
|
||||
.map(RefCell::borrow) else {return};
|
||||
.map(RefCell::borrow)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(smithay_tex) = renderer_surface_state
|
||||
.texture::<GlesRenderer>(renderer.id())
|
||||
.cloned() else {return};
|
||||
.cloned()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(sk_tex) = self.sk_tex.get() else {return};
|
||||
let Some(sk_mat) = self.sk_mat.get() else {return};
|
||||
let Some(sk_tex) = self.sk_tex.get() else {
|
||||
return;
|
||||
};
|
||||
let Some(sk_mat) = self.sk_mat.get() else {
|
||||
return;
|
||||
};
|
||||
unsafe {
|
||||
sk.tex_set_surface(
|
||||
sk_tex.as_ref(),
|
||||
@@ -149,7 +159,9 @@ impl CoreSurface {
|
||||
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 Some(surface_size) = renderer_surface_state.surface_size() else {
|
||||
return;
|
||||
};
|
||||
let new_mapped_data = CoreSurfaceData {
|
||||
size: Vector2::from([surface_size.w as u32, surface_size.h as u32]),
|
||||
wl_tex: Some(SendWrapper::new(smithay_tex)),
|
||||
@@ -164,7 +176,9 @@ impl CoreSurface {
|
||||
}
|
||||
|
||||
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(
|
||||
&wl_surface,
|
||||
@@ -196,10 +210,7 @@ impl CoreSurface {
|
||||
self.weak_surface.upgrade().ok()
|
||||
}
|
||||
|
||||
pub fn with_states<F, T>(&self, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&SurfaceData) -> T,
|
||||
{
|
||||
pub fn with_states<T, F: FnOnce(&SurfaceData) -> T>(&self, f: F) -> Option<T> {
|
||||
self.wl_surface()
|
||||
.map(|wl_surface| compositor::with_states(&wl_surface, f))
|
||||
}
|
||||
|
||||
14
src/wayland/utils.rs
Normal file
14
src/wayland/utils.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use smithay::{reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::compositor};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn insert_data<T: Send + Sync + 'static>(wl_surface: &WlSurface, data: T) {
|
||||
insert_data_raw(wl_surface, Arc::new(data))
|
||||
}
|
||||
pub fn insert_data_raw<T: Send + Sync + 'static>(wl_surface: &WlSurface, data: Arc<T>) {
|
||||
compositor::with_states(wl_surface, |d| {
|
||||
d.data_map.insert_if_missing_threadsafe(move || data)
|
||||
});
|
||||
}
|
||||
pub fn get_data<T: Send + Sync + 'static>(wl_surface: &WlSurface) -> Option<Arc<T>> {
|
||||
compositor::with_states(wl_surface, |d| d.data_map.get::<Arc<T>>().cloned())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
310
src/wayland/xdg_shell/backend.rs
Normal file
310
src/wayland/xdg_shell/backend.rs
Normal file
@@ -0,0 +1,310 @@
|
||||
use super::{popup::PopupData, surface::XdgSurfaceData, ToplevelData};
|
||||
use crate::{
|
||||
nodes::{
|
||||
data::KEYMAPS,
|
||||
drawable::model::ModelPart,
|
||||
items::panel::{Backend, ChildInfo, PanelItem, PanelItemInitData, SurfaceID},
|
||||
},
|
||||
wayland::{
|
||||
seat::{CursorInfo, KeyboardEvent, PointerEvent, SeatData},
|
||||
state::ClientState,
|
||||
surface::CoreSurface,
|
||||
utils, SERIAL_COUNTER,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel,
|
||||
wayland_server::{protocol::wl_surface::WlSurface, Resource, Weak},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::watch;
|
||||
use tracing::debug;
|
||||
|
||||
pub struct XdgToplevelState {
|
||||
pub fullscreen: bool,
|
||||
pub activated: bool,
|
||||
}
|
||||
|
||||
pub struct XdgBackend {
|
||||
toplevel: Weak<XdgToplevel>,
|
||||
toplevel_wl_surface: Weak<WlSurface>,
|
||||
pub toplevel_state: Mutex<XdgToplevelState>,
|
||||
popups: Mutex<FxHashMap<String, Weak<WlSurface>>>,
|
||||
pub cursor: watch::Receiver<Option<CursorInfo>>,
|
||||
pub seat: Arc<SeatData>,
|
||||
pointer_grab: Mutex<Option<SurfaceID>>,
|
||||
keyboard_grab: Mutex<Option<SurfaceID>>,
|
||||
}
|
||||
impl XdgBackend {
|
||||
pub fn create(
|
||||
toplevel_wl_surface: WlSurface,
|
||||
toplevel: XdgToplevel,
|
||||
seat: Arc<SeatData>,
|
||||
) -> Self {
|
||||
let cursor = seat.new_surface(&toplevel_wl_surface);
|
||||
XdgBackend {
|
||||
toplevel: toplevel.downgrade(),
|
||||
toplevel_wl_surface: toplevel_wl_surface.downgrade(),
|
||||
toplevel_state: Mutex::new(XdgToplevelState {
|
||||
fullscreen: false,
|
||||
activated: false,
|
||||
}),
|
||||
popups: Mutex::new(FxHashMap::default()),
|
||||
cursor,
|
||||
seat,
|
||||
pointer_grab: Mutex::new(None),
|
||||
keyboard_grab: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
||||
match id {
|
||||
SurfaceID::Cursor => self.cursor.borrow().as_ref()?.surface.upgrade().ok(),
|
||||
SurfaceID::Toplevel => self.toplevel_wl_surface(),
|
||||
SurfaceID::Child(popup) => {
|
||||
let popups = self.popups.lock();
|
||||
popups.get(popup)?.upgrade().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn toplevel_wl_surface(&self) -> Option<WlSurface> {
|
||||
self.toplevel_wl_surface.upgrade().ok()
|
||||
}
|
||||
|
||||
pub fn configure(&self, size: Option<Vector2<u32>>) {
|
||||
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(wl_surface) = self.toplevel_wl_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some(xdg_surface_data) = wl_surface.data::<XdgSurfaceData>() else {
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {
|
||||
return;
|
||||
};
|
||||
let Some(surface_size) = core_surface.size() else {
|
||||
return;
|
||||
};
|
||||
|
||||
xdg_toplevel.configure(
|
||||
size.unwrap_or(surface_size).x as i32,
|
||||
size.unwrap_or(surface_size).y as i32,
|
||||
self.states()
|
||||
.into_iter()
|
||||
.flat_map(|state| state.to_ne_bytes())
|
||||
.collect(),
|
||||
);
|
||||
xdg_surface_data.xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
self.flush_client();
|
||||
}
|
||||
fn states(&self) -> Vec<u32> {
|
||||
let mut states = vec![1, 5, 6, 7, 8]; // maximized always and tiled
|
||||
let toplevel_state = self.toplevel_state.lock();
|
||||
if toplevel_state.fullscreen {
|
||||
states.push(2);
|
||||
}
|
||||
if toplevel_state.activated {
|
||||
states.push(4);
|
||||
}
|
||||
states
|
||||
}
|
||||
|
||||
pub fn new_popup(
|
||||
&self,
|
||||
panel_item: &PanelItem<XdgBackend>,
|
||||
popup_wl_surface: &WlSurface,
|
||||
data: &PopupData,
|
||||
) {
|
||||
self.popups
|
||||
.lock()
|
||||
.insert(data.uid.clone(), popup_wl_surface.downgrade());
|
||||
|
||||
let Some(geometry) = data.geometry() else {
|
||||
return;
|
||||
};
|
||||
panel_item.new_child(
|
||||
&data.uid,
|
||||
ChildInfo {
|
||||
parent: utils::get_data::<SurfaceID>(&data.parent())
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.clone(),
|
||||
geometry,
|
||||
},
|
||||
)
|
||||
}
|
||||
pub fn reposition_popup(&self, panel_item: &PanelItem<XdgBackend>, popup_state: &PopupData) {
|
||||
let Some(geometry) = popup_state.geometry() else {
|
||||
return;
|
||||
};
|
||||
panel_item.reposition_child(&popup_state.uid, geometry)
|
||||
}
|
||||
pub fn drop_popup(&self, panel_item: &PanelItem<XdgBackend>, uid: &str) {
|
||||
panel_item.drop_child(uid);
|
||||
let Some(popup) = self.popups.lock().remove(uid) else {
|
||||
return;
|
||||
};
|
||||
let Some(wl_surface) = popup.upgrade().ok() else {
|
||||
return;
|
||||
};
|
||||
self.seat.drop_surface(&wl_surface);
|
||||
}
|
||||
|
||||
fn child_data(&self) -> FxHashMap<String, ChildInfo> {
|
||||
FxHashMap::from_iter(self.popups.lock().iter().filter_map(|(uid, v)| {
|
||||
let wl_surface = v.upgrade().ok()?;
|
||||
let popup_data = utils::get_data::<PopupData>(&wl_surface)?;
|
||||
let parent = utils::get_data::<SurfaceID>(&popup_data.parent())?
|
||||
.as_ref()
|
||||
.clone();
|
||||
let geometry = utils::get_data::<XdgSurfaceData>(&wl_surface)?
|
||||
.geometry
|
||||
.lock()
|
||||
.clone()?;
|
||||
Some((uid.clone(), ChildInfo { parent, geometry }))
|
||||
}))
|
||||
}
|
||||
|
||||
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 Drop for XdgBackend {
|
||||
fn drop(&mut self) {
|
||||
debug!("Dropped panel item gracefully");
|
||||
}
|
||||
}
|
||||
impl Backend for XdgBackend {
|
||||
fn start_data(&self) -> Result<PanelItemInitData> {
|
||||
let toplevel = self.toplevel_wl_surface();
|
||||
let toplevel_data = toplevel.as_ref().and_then(utils::get_data::<ToplevelData>);
|
||||
let toplevel_data = toplevel_data
|
||||
.as_deref()
|
||||
.clone()
|
||||
.ok_or_else(|| eyre!("Could not get toplevel"))?;
|
||||
|
||||
let pointer_grab = self.pointer_grab.lock().clone();
|
||||
let keyboard_grab = self.keyboard_grab.lock().clone();
|
||||
|
||||
Ok(PanelItemInitData {
|
||||
cursor: self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()),
|
||||
toplevel: toplevel_data.into(),
|
||||
children: self.child_data(),
|
||||
pointer_grab,
|
||||
keyboard_grab,
|
||||
})
|
||||
}
|
||||
|
||||
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 close_toplevel(&self) {
|
||||
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {
|
||||
return;
|
||||
};
|
||||
xdg_toplevel.close();
|
||||
}
|
||||
fn auto_size_toplevel(&self) {
|
||||
self.configure(Some([0, 0].into()));
|
||||
}
|
||||
fn set_toplevel_size(&self, size: Vector2<u32>) {
|
||||
self.configure(Some(size));
|
||||
}
|
||||
fn set_toplevel_focused_visuals(&self, focused: bool) {
|
||||
self.toplevel_state.lock().activated = focused;
|
||||
self.configure(None);
|
||||
}
|
||||
|
||||
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: key > 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.touch_down(&surface, id, position)
|
||||
}
|
||||
fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
||||
self.seat.touch_move(id, position)
|
||||
}
|
||||
fn touch_up(&self, id: u32) {
|
||||
self.seat.touch_up(id)
|
||||
}
|
||||
fn reset_touches(&self) {
|
||||
self.seat.reset_touches()
|
||||
}
|
||||
}
|
||||
69
src/wayland/xdg_shell/mod.rs
Normal file
69
src/wayland/xdg_shell/mod.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use self::{backend::XdgBackend, toplevel::ToplevelData};
|
||||
use super::state::WaylandState;
|
||||
use crate::wayland::{
|
||||
utils::insert_data,
|
||||
xdg_shell::{positioner::PositionerData, surface::XdgSurfaceData},
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_wm_base::{self, XdgWmBase},
|
||||
wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
mod backend;
|
||||
mod popup;
|
||||
mod positioner;
|
||||
mod surface;
|
||||
mod toplevel;
|
||||
|
||||
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
fn bind(
|
||||
_state: &mut WaylandState,
|
||||
_handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: New<XdgWmBase>,
|
||||
_global_data: &(),
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
_resource: &XdgWmBase,
|
||||
request: xdg_wm_base::Request,
|
||||
_data: &(),
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_wm_base::Request::CreatePositioner { id } => {
|
||||
let positioner = data_init.init(id, Mutex::new(PositionerData::default()));
|
||||
debug!(?positioner, "Create XDG positioner");
|
||||
}
|
||||
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
|
||||
let xdg_surface = data_init.init(id, surface.downgrade());
|
||||
debug!(?xdg_surface, "Create XDG surface");
|
||||
insert_data(
|
||||
&surface,
|
||||
XdgSurfaceData {
|
||||
wl_surface: surface.downgrade(),
|
||||
xdg_surface,
|
||||
geometry: Mutex::new(None),
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_wm_base::Request::Pong { serial } => {
|
||||
debug!(serial, "Client pong");
|
||||
}
|
||||
xdg_wm_base::Request::Destroy => {
|
||||
debug!("Destroy XDG WM base");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/wayland/xdg_shell/popup.rs
Normal file
119
src/wayland/xdg_shell/popup.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use super::{backend::XdgBackend, positioner::PositionerData};
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, SurfaceID},
|
||||
wayland::{state::WaylandState, utils::get_data},
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::XdgPositioner,
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{debug, error};
|
||||
use wayland_backend::server::ClientId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PopupData {
|
||||
pub uid: String,
|
||||
grabbed: Mutex<bool>,
|
||||
parent: Mutex<WlWeak<WlSurface>>,
|
||||
panel_item: Weak<PanelItem<XdgBackend>>,
|
||||
positioner: Mutex<XdgPositioner>,
|
||||
}
|
||||
impl PopupData {
|
||||
pub fn new(
|
||||
uid: impl ToString,
|
||||
parent: WlSurface,
|
||||
panel_item: &Arc<PanelItem<XdgBackend>>,
|
||||
positioner: XdgPositioner,
|
||||
) -> Self {
|
||||
PopupData {
|
||||
uid: uid.to_string(),
|
||||
grabbed: Mutex::new(false),
|
||||
parent: Mutex::new(parent.downgrade()),
|
||||
panel_item: Arc::downgrade(panel_item),
|
||||
positioner: Mutex::new(positioner),
|
||||
}
|
||||
}
|
||||
pub fn geometry(&self) -> Option<Geometry> {
|
||||
let positioner = self.positioner.lock().clone();
|
||||
let positioner_data = positioner.data::<Mutex<PositionerData>>()?.lock();
|
||||
Some(positioner_data.clone().into())
|
||||
}
|
||||
pub fn parent(&self) -> WlSurface {
|
||||
self.parent.lock().upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPopup, WlWeak<WlSurface>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
xdg_popup: &XdgPopup,
|
||||
request: xdg_popup::Request,
|
||||
wl_surface_resource: &WlWeak<WlSurface>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let Ok(wl_surface) = wl_surface_resource.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg popup");
|
||||
return;
|
||||
};
|
||||
let Some(popup_data) = get_data::<PopupData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgPopup");
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = popup_data.panel_item.upgrade() else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
match request {
|
||||
xdg_popup::Request::Grab { seat, serial } => {
|
||||
*popup_data.grabbed.lock() = true;
|
||||
debug!(?xdg_popup, ?seat, serial, "XDG popup grab");
|
||||
panel_item.grab_keyboard(Some(SurfaceID::Child(popup_data.uid.clone())));
|
||||
}
|
||||
xdg_popup::Request::Reposition { positioner, token } => {
|
||||
debug!(?xdg_popup, ?positioner, token, "XDG popup reposition");
|
||||
*popup_data.positioner.lock() = positioner;
|
||||
panel_item
|
||||
.backend
|
||||
.reposition_popup(&panel_item, &popup_data);
|
||||
}
|
||||
xdg_popup::Request::Destroy => {
|
||||
debug!(?xdg_popup, "Destroy XDG popup");
|
||||
if *popup_data.grabbed.lock() {
|
||||
panel_item.grab_keyboard(None);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut WaylandState,
|
||||
_client: ClientId,
|
||||
_popup: &XdgPopup,
|
||||
data: &WlWeak<WlSurface>,
|
||||
) {
|
||||
let Ok(wl_surface) = data.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg popup");
|
||||
return;
|
||||
};
|
||||
let Some(popup_data) = get_data::<PopupData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgPopup");
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = popup_data.panel_item.upgrade() else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.drop_popup(&panel_item, &popup_data.uid);
|
||||
}
|
||||
}
|
||||
226
src/wayland/xdg_shell/positioner.rs
Normal file
226
src/wayland/xdg_shell/positioner.rs
Normal file
@@ -0,0 +1,226 @@
|
||||
use crate::{nodes::items::panel::Geometry, wayland::state::WaylandState};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_positioner::{
|
||||
self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner,
|
||||
},
|
||||
wayland_server::{Client, DataInit, Dispatch, DisplayHandle, Resource},
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
use wayland_backend::protocol::WEnum;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PositionerData {
|
||||
size: Vector2<u32>,
|
||||
anchor_rect_pos: Vector2<i32>,
|
||||
anchor_rect_size: Vector2<u32>,
|
||||
anchor: Anchor,
|
||||
gravity: Gravity,
|
||||
constraint_adjustment: ConstraintAdjustment,
|
||||
offset: Vector2<i32>,
|
||||
reactive: bool,
|
||||
}
|
||||
impl Default for PositionerData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size: Vector2::from([0; 2]),
|
||||
anchor_rect_pos: Vector2::from([0; 2]),
|
||||
anchor_rect_size: Vector2::from([0; 2]),
|
||||
anchor: Anchor::None,
|
||||
gravity: Gravity::None,
|
||||
constraint_adjustment: ConstraintAdjustment::None,
|
||||
offset: Vector2::from([0; 2]),
|
||||
reactive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PositionerData {
|
||||
fn anchor_has_edge(&self, edge: Anchor) -> bool {
|
||||
match edge {
|
||||
Anchor::Top => {
|
||||
self.anchor == Anchor::Top
|
||||
|| self.anchor == Anchor::TopLeft
|
||||
|| self.anchor == Anchor::TopRight
|
||||
}
|
||||
Anchor::Bottom => {
|
||||
self.anchor == Anchor::Bottom
|
||||
|| self.anchor == Anchor::BottomLeft
|
||||
|| self.anchor == Anchor::BottomRight
|
||||
}
|
||||
Anchor::Left => {
|
||||
self.anchor == Anchor::Left
|
||||
|| self.anchor == Anchor::TopLeft
|
||||
|| self.anchor == Anchor::BottomLeft
|
||||
}
|
||||
Anchor::Right => {
|
||||
self.anchor == Anchor::Right
|
||||
|| self.anchor == Anchor::TopRight
|
||||
|| self.anchor == Anchor::BottomRight
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gravity_has_edge(&self, edge: Gravity) -> bool {
|
||||
match edge {
|
||||
Gravity::Top => {
|
||||
self.gravity == Gravity::Top
|
||||
|| self.gravity == Gravity::TopLeft
|
||||
|| self.gravity == Gravity::TopRight
|
||||
}
|
||||
Gravity::Bottom => {
|
||||
self.gravity == Gravity::Bottom
|
||||
|| self.gravity == Gravity::BottomLeft
|
||||
|| self.gravity == Gravity::BottomRight
|
||||
}
|
||||
Gravity::Left => {
|
||||
self.gravity == Gravity::Left
|
||||
|| self.gravity == Gravity::TopLeft
|
||||
|| self.gravity == Gravity::BottomLeft
|
||||
}
|
||||
Gravity::Right => {
|
||||
self.gravity == Gravity::Right
|
||||
|| self.gravity == Gravity::TopRight
|
||||
|| self.gravity == Gravity::BottomRight
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> Vector2<i32> {
|
||||
let mut pos = self.offset;
|
||||
|
||||
if self.anchor_has_edge(Anchor::Top) {
|
||||
pos.y += self.anchor_rect_pos.y;
|
||||
} else if self.anchor_has_edge(Anchor::Bottom) {
|
||||
pos.y += self.anchor_rect_pos.y + self.anchor_rect_size.y as i32;
|
||||
} else {
|
||||
pos.y += self.anchor_rect_pos.y + self.anchor_rect_size.y as i32 / 2;
|
||||
}
|
||||
|
||||
if self.anchor_has_edge(Anchor::Left) {
|
||||
pos.x += self.anchor_rect_pos.x;
|
||||
} else if self.anchor_has_edge(Anchor::Right) {
|
||||
pos.x += self.anchor_rect_pos.x + self.anchor_rect_size.x as i32;
|
||||
} else {
|
||||
pos.x += self.anchor_rect_pos.x + self.anchor_rect_size.x as i32 / 2;
|
||||
}
|
||||
|
||||
if self.gravity_has_edge(Gravity::Top) {
|
||||
pos.y -= self.size.y as i32;
|
||||
} else if !self.gravity_has_edge(Gravity::Bottom) {
|
||||
pos.y -= self.size.y as i32 / 2;
|
||||
}
|
||||
|
||||
if self.gravity_has_edge(Gravity::Left) {
|
||||
pos.x -= self.size.x as i32;
|
||||
} else if !self.gravity_has_edge(Gravity::Right) {
|
||||
pos.x -= self.size.x as i32 / 2;
|
||||
}
|
||||
|
||||
pos
|
||||
}
|
||||
}
|
||||
impl From<PositionerData> for Geometry {
|
||||
fn from(value: PositionerData) -> Self {
|
||||
Geometry {
|
||||
origin: value.get_pos(),
|
||||
size: value.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPositioner, Mutex<PositionerData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
positioner: &XdgPositioner,
|
||||
request: xdg_positioner::Request,
|
||||
data: &Mutex<PositionerData>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_positioner::Request::SetSize { width, height } => {
|
||||
debug!(?positioner, width, height, "Set positioner size");
|
||||
data.lock().size = Vector2::from([width as u32, height as u32]);
|
||||
}
|
||||
xdg_positioner::Request::SetAnchorRect {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
if width < 1 || height < 1 {
|
||||
positioner.post_error(
|
||||
xdg_positioner::Error::InvalidInput,
|
||||
"Invalid size for positioner's anchor rectangle.",
|
||||
);
|
||||
warn!(
|
||||
?positioner,
|
||||
width, height, "Invalid size for positioner's anchor rectangle"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(
|
||||
?positioner,
|
||||
x, y, width, height, "Set positioner anchor rectangle"
|
||||
);
|
||||
let mut data = data.lock();
|
||||
data.anchor_rect_pos = [x, y].into();
|
||||
data.anchor_rect_size = [width as u32, height as u32].into();
|
||||
}
|
||||
xdg_positioner::Request::SetAnchor { anchor } => {
|
||||
if let WEnum::Value(anchor) = anchor {
|
||||
debug!(?positioner, ?anchor, "Set positioner anchor");
|
||||
data.lock().anchor = anchor;
|
||||
}
|
||||
}
|
||||
xdg_positioner::Request::SetGravity { gravity } => {
|
||||
if let WEnum::Value(gravity) = gravity {
|
||||
debug!(?positioner, ?gravity, "Set positioner gravity");
|
||||
data.lock().gravity = gravity;
|
||||
}
|
||||
}
|
||||
xdg_positioner::Request::SetConstraintAdjustment {
|
||||
constraint_adjustment,
|
||||
} => {
|
||||
debug!(
|
||||
?positioner,
|
||||
constraint_adjustment, "Set positioner constraint adjustment"
|
||||
);
|
||||
let Some(constraint_adjustment) =
|
||||
ConstraintAdjustment::from_bits(constraint_adjustment)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
data.lock().constraint_adjustment = constraint_adjustment;
|
||||
}
|
||||
xdg_positioner::Request::SetOffset { x, y } => {
|
||||
debug!(?positioner, x, y, "Set positioner offset");
|
||||
data.lock().offset = [x, y].into();
|
||||
}
|
||||
xdg_positioner::Request::SetReactive => {
|
||||
debug!(?positioner, "Set positioner reactive");
|
||||
data.lock().reactive = true;
|
||||
}
|
||||
xdg_positioner::Request::SetParentSize {
|
||||
parent_width,
|
||||
parent_height,
|
||||
} => {
|
||||
debug!(
|
||||
?positioner,
|
||||
parent_width, parent_height, "Set positioner parent size"
|
||||
);
|
||||
}
|
||||
xdg_positioner::Request::SetParentConfigure { serial } => {
|
||||
debug!(?positioner, serial, "Set positioner parent size");
|
||||
}
|
||||
xdg_positioner::Request::Destroy => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
236
src/wayland/xdg_shell/surface.rs
Normal file
236
src/wayland/xdg_shell/surface.rs
Normal file
@@ -0,0 +1,236 @@
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, SurfaceID},
|
||||
wayland::{
|
||||
seat::handle_cursor,
|
||||
state::{ClientState, WaylandState},
|
||||
surface::CoreSurface,
|
||||
utils,
|
||||
xdg_shell::{popup::PopupData, toplevel::ToplevelData, XdgBackend},
|
||||
SERIAL_COUNTER,
|
||||
},
|
||||
};
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::{
|
||||
xdg_surface::{self, XdgSurface},
|
||||
xdg_toplevel::{XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{debug, error};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XdgSurfaceData {
|
||||
pub wl_surface: WlWeak<WlSurface>,
|
||||
pub xdg_surface: XdgSurface,
|
||||
pub geometry: Mutex<Option<Geometry>>,
|
||||
}
|
||||
|
||||
impl Dispatch<XdgSurface, WlWeak<WlSurface>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
state: &mut WaylandState,
|
||||
client: &Client,
|
||||
xdg_surface: &XdgSurface,
|
||||
request: xdg_surface::Request,
|
||||
wl_surface_resource: &WlWeak<WlSurface>,
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let Ok(wl_surface) = wl_surface_resource.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg surface");
|
||||
return;
|
||||
};
|
||||
let Some(xdg_surface_data) = utils::get_data::<XdgSurfaceData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgSurface");
|
||||
return;
|
||||
};
|
||||
match request {
|
||||
xdg_surface::Request::GetToplevel { id } => {
|
||||
let toplevel = data_init.init(id, wl_surface_resource.clone());
|
||||
utils::insert_data(&wl_surface, SurfaceID::Toplevel);
|
||||
utils::insert_data(&wl_surface, toplevel.clone());
|
||||
utils::insert_data(&wl_surface, ToplevelData::new(&wl_surface));
|
||||
debug!(?toplevel, ?xdg_surface, "Create XDG toplevel");
|
||||
|
||||
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
|
||||
toplevel.wm_capabilities(vec![3]);
|
||||
}
|
||||
toplevel.configure(
|
||||
0,
|
||||
0,
|
||||
if toplevel.version() >= 2 {
|
||||
vec![1, 5, 6, 7, 8]
|
||||
.into_iter()
|
||||
.flat_map(u32::to_ne_bytes)
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
|
||||
let client_credentials = client.get_credentials(&state.display_handle).ok();
|
||||
let Some(seat_data) = client.get_data::<ClientState>().map(|s| s.seat.clone())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let xdg_surface = xdg_surface.clone();
|
||||
CoreSurface::add_to(
|
||||
state.display_handle.clone(),
|
||||
&wl_surface,
|
||||
{
|
||||
let wl_surface_resource = wl_surface_resource.clone();
|
||||
move || {
|
||||
let wl_surface = wl_surface_resource.upgrade().unwrap();
|
||||
|
||||
let backend = XdgBackend::create(
|
||||
wl_surface.clone(),
|
||||
toplevel.clone(),
|
||||
seat_data.clone(),
|
||||
);
|
||||
let (node, panel_item) = PanelItem::create(
|
||||
Box::new(backend),
|
||||
client_credentials.map(|c| c.pid),
|
||||
);
|
||||
utils::insert_data(&wl_surface, Arc::downgrade(&panel_item));
|
||||
utils::insert_data_raw(&wl_surface, node);
|
||||
handle_cursor(&panel_item, panel_item.backend.cursor.clone());
|
||||
}
|
||||
},
|
||||
{
|
||||
let wl_surface_resource = wl_surface_resource.clone();
|
||||
move |_| {
|
||||
let wl_surface = wl_surface_resource.upgrade().unwrap();
|
||||
|
||||
let Some(panel_item) =
|
||||
utils::get_data::<PanelItem<XdgBackend>>(&wl_surface)
|
||||
else {
|
||||
let Some(toplevel) = utils::get_data::<XdgToplevel>(&wl_surface)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
toplevel.configure(
|
||||
0,
|
||||
0,
|
||||
if toplevel.version() >= 2 {
|
||||
vec![5, 6, 7, 8]
|
||||
.into_iter()
|
||||
.flat_map(u32::to_ne_bytes)
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(size) = core_surface.size() else {
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_size_changed(size);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_surface::Request::GetPopup {
|
||||
id,
|
||||
parent,
|
||||
positioner,
|
||||
} => {
|
||||
let Some(parent) = parent else { return };
|
||||
let Some(parent_wl_surface) = parent
|
||||
.data::<WlWeak<WlSurface>>()
|
||||
.map(WlWeak::upgrade)
|
||||
.map(Result::ok)
|
||||
.flatten()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) =
|
||||
utils::get_data::<Weak<PanelItem<XdgBackend>>>(&parent_wl_surface)
|
||||
.as_deref()
|
||||
.and_then(Weak::upgrade)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let uid = nanoid!();
|
||||
let popup_data = PopupData::new(
|
||||
uid.clone(),
|
||||
parent_wl_surface.clone(),
|
||||
&panel_item,
|
||||
positioner,
|
||||
);
|
||||
handle_cursor(
|
||||
&panel_item,
|
||||
panel_item.backend.seat.new_surface(&wl_surface),
|
||||
);
|
||||
let xdg_popup = data_init.init(id, wl_surface.downgrade());
|
||||
utils::insert_data(&wl_surface, SurfaceID::Child(uid));
|
||||
utils::insert_data(&wl_surface, Arc::downgrade(&panel_item));
|
||||
utils::insert_data(&wl_surface, popup_data);
|
||||
utils::insert_data(&wl_surface, xdg_popup.clone());
|
||||
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
|
||||
|
||||
let xdg_surface = xdg_surface.downgrade();
|
||||
let popup_wl_surface = wl_surface.downgrade();
|
||||
CoreSurface::add_to(
|
||||
state.display_handle.clone(),
|
||||
&wl_surface,
|
||||
move || {
|
||||
let Ok(wl_surface) = popup_wl_surface.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(popup_data) = utils::get_data::<PopupData>(&wl_surface) else {
|
||||
return;
|
||||
};
|
||||
panel_item
|
||||
.backend
|
||||
.new_popup(&panel_item, &wl_surface, &*popup_data);
|
||||
},
|
||||
move |commit_count| {
|
||||
if commit_count == 0 {
|
||||
if let Ok(xdg_surface) = xdg_surface.upgrade() {
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc())
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_surface::Request::SetWindowGeometry {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
debug!(
|
||||
?xdg_surface,
|
||||
x, y, width, height, "Set XDG surface geometry"
|
||||
);
|
||||
let geometry = Geometry {
|
||||
origin: [x, y].into(),
|
||||
size: [width as u32, height as u32].into(),
|
||||
};
|
||||
xdg_surface_data.geometry.lock().replace(geometry);
|
||||
}
|
||||
xdg_surface::Request::AckConfigure { serial } => {
|
||||
debug!(?xdg_surface, serial, "Acknowledge XDG surface configure");
|
||||
}
|
||||
xdg_surface::Request::Destroy => {
|
||||
debug!(?xdg_surface, "Destroy XDG surface");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
239
src/wayland/xdg_shell/toplevel.rs
Normal file
239
src/wayland/xdg_shell/toplevel.rs
Normal file
@@ -0,0 +1,239 @@
|
||||
use super::{backend::XdgBackend, surface::XdgSurfaceData};
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, ToplevelInfo},
|
||||
wayland::{
|
||||
state::WaylandState,
|
||||
surface::CoreSurface,
|
||||
utils::{self, get_data},
|
||||
},
|
||||
};
|
||||
use mint::Vector2;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge, XdgToplevel},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
};
|
||||
use std::sync::Weak;
|
||||
use tracing::{debug, error};
|
||||
use wayland_backend::protocol::WEnum;
|
||||
|
||||
pub struct ToplevelData {
|
||||
panel_item: OnceCell<Weak<PanelItem<XdgBackend>>>,
|
||||
wl_surface: WlWeak<WlSurface>,
|
||||
parent: Mutex<Option<WlWeak<WlSurface>>>,
|
||||
title: Mutex<Option<String>>,
|
||||
app_id: Mutex<Option<String>>,
|
||||
max_size: Mutex<Option<Vector2<u32>>>,
|
||||
min_size: Mutex<Option<Vector2<u32>>>,
|
||||
}
|
||||
impl ToplevelData {
|
||||
pub fn new(wl_surface: &WlSurface) -> Self {
|
||||
ToplevelData {
|
||||
panel_item: OnceCell::new(),
|
||||
wl_surface: wl_surface.downgrade(),
|
||||
parent: Mutex::new(None),
|
||||
title: Mutex::new(None),
|
||||
app_id: Mutex::new(None),
|
||||
max_size: Mutex::new(None),
|
||||
min_size: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
pub fn parent(&self) -> Option<WlSurface> {
|
||||
self.parent
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map(WlWeak::upgrade)
|
||||
.map(Result::ok)
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
impl From<&ToplevelData> for ToplevelInfo {
|
||||
fn from(value: &ToplevelData) -> Self {
|
||||
let wl_surface = value.wl_surface.upgrade().ok();
|
||||
let size = CoreSurface::from_wl_surface(wl_surface.as_ref().unwrap())
|
||||
.unwrap()
|
||||
.size()
|
||||
.unwrap();
|
||||
let logical_rectangle = wl_surface
|
||||
.as_ref()
|
||||
.and_then(utils::get_data::<XdgSurfaceData>)
|
||||
.and_then(|d| d.geometry.lock().clone())
|
||||
.unwrap_or_else(|| Geometry {
|
||||
origin: [0, 0].into(),
|
||||
size,
|
||||
});
|
||||
let parent = value
|
||||
.parent()
|
||||
.as_ref()
|
||||
.and_then(utils::get_data::<Weak<PanelItem<XdgBackend>>>)
|
||||
.as_deref()
|
||||
.and_then(Weak::upgrade)
|
||||
.map(|i| i.uid.clone());
|
||||
ToplevelInfo {
|
||||
parent,
|
||||
title: value.title.lock().clone(),
|
||||
app_id: value.app_id.lock().clone(),
|
||||
size,
|
||||
min_size: value.min_size.lock().clone(),
|
||||
max_size: value.max_size.lock().clone(),
|
||||
logical_rectangle,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for ToplevelData {
|
||||
fn drop(&mut self) {
|
||||
// let Some(panel_item) = self.panel_item.get().and_then(Weak::upgrade) else {
|
||||
// return;
|
||||
// };
|
||||
// panel_item.drop_toplevel();
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgToplevel, WlWeak<WlSurface>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
xdg_toplevel: &XdgToplevel,
|
||||
request: xdg_toplevel::Request,
|
||||
wl_surface_resource: &WlWeak<WlSurface>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let Ok(wl_surface) = wl_surface_resource.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg toplevel");
|
||||
return;
|
||||
};
|
||||
let Some(toplevel_data) = utils::get_data::<ToplevelData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgToplevel");
|
||||
return;
|
||||
};
|
||||
match request {
|
||||
xdg_toplevel::Request::SetParent { parent } => {
|
||||
debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent");
|
||||
let Some(parent_xdg_toplevel) = parent else {
|
||||
*toplevel_data.parent.lock() = None;
|
||||
return;
|
||||
};
|
||||
let Some(parent_toplevel_data) = parent_xdg_toplevel.data::<ToplevelData>() else {
|
||||
error!("Couldn't get XDG toplevel parent data");
|
||||
return;
|
||||
};
|
||||
let Ok(parent_wl_surface) = parent_toplevel_data.wl_surface.upgrade() else {
|
||||
error!("Couldn't get XDG toplevel parent wl surface");
|
||||
return;
|
||||
};
|
||||
*toplevel_data.parent.lock() = Some(parent_wl_surface.downgrade());
|
||||
let Some(parent_panel_item) = parent_toplevel_data
|
||||
.panel_item
|
||||
.get()
|
||||
.and_then(Weak::upgrade)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_parent_changed(&parent_panel_item.uid);
|
||||
}
|
||||
xdg_toplevel::Request::SetTitle { title } => {
|
||||
debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title");
|
||||
*toplevel_data.title.lock() = (!title.is_empty()).then_some(title.clone());
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_title_changed(&title);
|
||||
}
|
||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||
debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID");
|
||||
*toplevel_data.app_id.lock() = (!app_id.is_empty()).then_some(app_id.clone());
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_app_id_changed(&app_id);
|
||||
}
|
||||
xdg_toplevel::Request::Move { seat, serial } => {
|
||||
debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request");
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_move_request();
|
||||
}
|
||||
xdg_toplevel::Request::Resize {
|
||||
seat,
|
||||
serial,
|
||||
edges,
|
||||
} => {
|
||||
let WEnum::Value(edges) = edges else { return };
|
||||
debug!(
|
||||
?xdg_toplevel,
|
||||
?seat,
|
||||
serial,
|
||||
?edges,
|
||||
"XDG Toplevel resize request"
|
||||
);
|
||||
let (up, down, left, right) = match edges {
|
||||
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),
|
||||
};
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_resize_request(up, down, left, right)
|
||||
}
|
||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size");
|
||||
*toplevel_data.max_size.lock() = (width > 1 || height > 1)
|
||||
.then_some(Vector2::from([width as u32, height as u32]));
|
||||
}
|
||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel min size");
|
||||
*toplevel_data.min_size.lock() = (width > 1 || height > 1)
|
||||
.then_some(Vector2::from([width as u32, height as u32]));
|
||||
}
|
||||
xdg_toplevel::Request::SetFullscreen { output: _ } => {
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.toplevel_state.lock().fullscreen = true;
|
||||
panel_item.backend.configure(None);
|
||||
panel_item.toplevel_fullscreen_active(true);
|
||||
}
|
||||
xdg_toplevel::Request::UnsetFullscreen => {
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.toplevel_state.lock().fullscreen = false;
|
||||
panel_item.backend.configure(None);
|
||||
panel_item.toplevel_fullscreen_active(false);
|
||||
}
|
||||
xdg_toplevel::Request::Destroy => {
|
||||
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.seat.drop_surface(&wl_surface);
|
||||
panel_item.drop_toplevel();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user