Compare commits
124 commits
1c21bd1259
...
c5dd462a6e
| Author | SHA1 | Date | |
|---|---|---|---|
| c5dd462a6e | |||
| c4e9011714 | |||
| 8f7997e270 | |||
| 4074cee365 | |||
| fa1ad20864 | |||
| 112b1a8e5f | |||
| 100c657050 | |||
| 9437a56807 | |||
| ea7251d6e0 | |||
| 7531c8f791 | |||
| 556e4214c4 | |||
| 4e4de21c2e | |||
| 7a49da0a48 | |||
| 774177390e | |||
| 795cc7d117 | |||
| ed0dc3fbad | |||
| 959f23f61b | |||
| 8c61a3150b | |||
| 1e9ed0e568 | |||
| 097eb2e8d3 | |||
| 3c7ba1a7b7 | |||
| 1a43bd55e1 | |||
| ff04218023 | |||
| 054a3a919f | |||
| d36ec3b7d3 | |||
| c482a2b99d | |||
| 7d9ee6e696 | |||
| 16c402825f | |||
| c4655d4641 | |||
| 36eb811f25 | |||
| c2d86dcd7f | |||
| cd1f049a3e | |||
| 27750a31c4 | |||
| 19c2265400 | |||
| fb31d5115d | |||
| b387c57d57 | |||
| b0603f0639 | |||
| e78e9bcd4c | |||
| 08b37552e2 | |||
| d74c56fc76 | |||
| 32ad879a65 | |||
| ec9ce08c77 | |||
| 05c40cb46d | |||
| 274e7c0602 | |||
| b08a8b63c9 | |||
| 814ba579bf | |||
| 988a1e6965 | |||
| 1d739ac4b6 | |||
| 6393fdf3c0 | |||
| 0016bc8cf0 | |||
| f4fdf750ec | |||
| 899d39a320 | |||
| b5b690d9cf | |||
| e3f122e903 | |||
| bcc85c8b1b | |||
| 864f506b1e | |||
| 9135834cc7 | |||
| 869a2c2428 | |||
| 3bc3d7ac2a | |||
| 5f02f22c8b | |||
| ce03990ea4 | |||
| 698110c265 | |||
| 8a35bc3ca4 | |||
| 6f913d5f69 | |||
| 5cb7b5973f | |||
| f9e8d614ce | |||
| a8176b96c3 | |||
| 87de5fcca3 | |||
| d8920ed7a0 | |||
| a038855895 | |||
| c8a6b69bf1 | |||
| 36b5a831fc | |||
| e21670f3f6 | |||
| d9261414c2 | |||
| f0e7bd31cb | |||
| 81a1a865a1 | |||
| fb65585bfa | |||
| e94d8fec1f | |||
| 41e7fcc290 | |||
| b550bb1025 | |||
| 902853955b | |||
| 524836bef3 | |||
| 03f35d7944 | |||
| be1511a7be | |||
| 46d39becd4 | |||
| a20deb0628 | |||
| 59e4e6dfb7 | |||
| e996d9528a | |||
| 9606e0892c | |||
| 7d9cd198ba | |||
| 151dc313ba | |||
| 54aefd8c41 | |||
| db94c9167f | |||
| 1558666601 | |||
| 81a718aa74 | |||
| 6d569bd4b7 | |||
| 11940fb6a5 | |||
| 663cfb3ca3 | |||
| bf3859a026 | |||
| 37ec1a4a3f | |||
| 854e0474be | |||
| f5bcfaf73c | |||
| 4562a34890 | |||
| ebaccd8762 | |||
| f456905231 | |||
| 061991218f | |||
| d50863bbd8 | |||
| c709ff997f | |||
| 5a3b2a483b | |||
| 1c24ca26fe | |||
| aa70204881 | |||
| 89dc6c91cf | |||
| a1e4641e82 | |||
| d8380b3dce | |||
| 61ec13def0 | |||
| 9e428510ca | |||
| 6489c2821c | |||
| c3b17db151 | |||
| 03d3876888 | |||
| b7ecf700fa | |||
| 657e7ce2f7 | |||
| 2a079ed800 | |||
| eece44a59c | |||
| 2167484861 |
702 changed files with 34186 additions and 40630 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
|
@ -1,3 +1,3 @@
|
||||||
[submodule "toml-config/toml-test"]
|
[submodule "crates/toml-config/toml-test"]
|
||||||
path = toml-config/toml-test
|
path = crates/toml-config/toml-test
|
||||||
url = https://github.com/mahkoh/toml-tests.git
|
url = https://github.com/mahkoh/toml-tests.git
|
||||||
|
|
|
||||||
486
Cargo.lock
generated
486
Cargo.lock
generated
|
|
@ -630,6 +630,16 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-allocator"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-formats",
|
||||||
|
"jay-video-types",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-ash"
|
name = "jay-ash"
|
||||||
version = "0.3.0+1.4.344"
|
version = "0.3.0+1.4.344"
|
||||||
|
|
@ -639,6 +649,52 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-async-engine"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-time",
|
||||||
|
"jay-tracy",
|
||||||
|
"jay-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-bufio"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-bugs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-clientmem"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-cpu-worker",
|
||||||
|
"jay-gfx-types",
|
||||||
|
"jay-tracy",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-cmm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-compositor"
|
name = "jay-compositor"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
|
|
@ -664,9 +720,48 @@ dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"isnt 0.2.0",
|
"isnt 0.2.0",
|
||||||
"jay-algorithms",
|
"jay-algorithms",
|
||||||
|
"jay-allocator",
|
||||||
"jay-ash",
|
"jay-ash",
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-bufio",
|
||||||
|
"jay-bugs",
|
||||||
|
"jay-clientmem",
|
||||||
|
"jay-cmm",
|
||||||
"jay-config",
|
"jay-config",
|
||||||
|
"jay-cpu-worker",
|
||||||
|
"jay-criteria",
|
||||||
|
"jay-damage",
|
||||||
|
"jay-dbus-core",
|
||||||
|
"jay-drm-feedback",
|
||||||
|
"jay-edid",
|
||||||
|
"jay-eventfd-cache",
|
||||||
|
"jay-formats",
|
||||||
|
"jay-geometry",
|
||||||
|
"jay-gfx-types",
|
||||||
|
"jay-input-types",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-keyboard",
|
||||||
|
"jay-layout-animation",
|
||||||
|
"jay-libinput",
|
||||||
|
"jay-logger",
|
||||||
|
"jay-output-schedule",
|
||||||
|
"jay-output-types",
|
||||||
|
"jay-pango",
|
||||||
|
"jay-pr-caps",
|
||||||
|
"jay-sighand",
|
||||||
|
"jay-theme",
|
||||||
|
"jay-time",
|
||||||
"jay-toml-config",
|
"jay-toml-config",
|
||||||
|
"jay-tracy",
|
||||||
|
"jay-tree-types",
|
||||||
|
"jay-udmabuf",
|
||||||
|
"jay-units",
|
||||||
|
"jay-utils",
|
||||||
|
"jay-video-types",
|
||||||
|
"jay-wheel",
|
||||||
|
"jay-wire-buf",
|
||||||
|
"jay-wire-types",
|
||||||
|
"jay-xcon",
|
||||||
"kbvm",
|
"kbvm",
|
||||||
"libloading",
|
"libloading",
|
||||||
"linearize",
|
"linearize",
|
||||||
|
|
@ -682,13 +777,11 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"repc",
|
"repc",
|
||||||
"run-on-drop",
|
"run-on-drop",
|
||||||
"rustc-demangle",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
"tracy-client-sys",
|
|
||||||
"uapi",
|
"uapi",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"with_builtin_macros",
|
"with_builtin_macros",
|
||||||
|
|
@ -699,7 +792,6 @@ name = "jay-config"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bincode",
|
|
||||||
"bstr",
|
"bstr",
|
||||||
"error_reporter",
|
"error_reporter",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
|
@ -711,6 +803,279 @@ dependencies = [
|
||||||
"uapi",
|
"uapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-config-schema"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"jay-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-cpu-worker"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-geometry",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-tracy",
|
||||||
|
"jay-utils",
|
||||||
|
"jay-wheel",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-criteria"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"jay-utils",
|
||||||
|
"linearize",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-damage"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-geometry",
|
||||||
|
"jay-tree-types",
|
||||||
|
"jay-units",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-dbus-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"jay-bufio",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-drm-feedback"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"byteorder",
|
||||||
|
"jay-video-types",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-edid"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-eventfd-cache"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-formats"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"clap",
|
||||||
|
"jay-ash",
|
||||||
|
"jay-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-geometry"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-algorithms",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-gfx-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-input-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-output-types",
|
||||||
|
"jay-units",
|
||||||
|
"jay-utils",
|
||||||
|
"linearize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-io-uring"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-time",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"run-on-drop",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-keyboard"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"blake3",
|
||||||
|
"jay-input-types",
|
||||||
|
"jay-utils",
|
||||||
|
"kbvm",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-layout-animation"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-geometry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-libinput"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bstr",
|
||||||
|
"isnt 0.2.0",
|
||||||
|
"jay-utils",
|
||||||
|
"libloading",
|
||||||
|
"log",
|
||||||
|
"repc",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-logger"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"bstr",
|
||||||
|
"clap",
|
||||||
|
"dirs",
|
||||||
|
"humantime",
|
||||||
|
"jay-config",
|
||||||
|
"jay-utils",
|
||||||
|
"linearize",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-output-schedule"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-output-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"blake3",
|
||||||
|
"jay-cmm",
|
||||||
|
"jay-formats",
|
||||||
|
"jay-utils",
|
||||||
|
"linearize",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-pango"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"jay-geometry",
|
||||||
|
"repc",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-pr-caps"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-utils",
|
||||||
|
"opera",
|
||||||
|
"parking_lot",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-sighand"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-theme"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-cmm",
|
||||||
|
"jay-config",
|
||||||
|
"jay-gfx-types",
|
||||||
|
"jay-utils",
|
||||||
|
"linearize",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-time"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-toml"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"indexmap",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-toml-config"
|
name = "jay-toml-config"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
|
@ -720,15 +1085,126 @@ dependencies = [
|
||||||
"error_reporter",
|
"error_reporter",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"jay-config",
|
"jay-config",
|
||||||
|
"jay-config-schema",
|
||||||
|
"jay-toml",
|
||||||
"kbvm",
|
"kbvm",
|
||||||
"log",
|
"log",
|
||||||
"phf",
|
"phf",
|
||||||
"run-on-drop",
|
"run-on-drop",
|
||||||
"serde_json",
|
|
||||||
"simplelog",
|
"simplelog",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uapi",
|
"uapi",
|
||||||
"walkdir",
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-tracy"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"parking_lot",
|
||||||
|
"rustc-demangle",
|
||||||
|
"tracy-client-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-tree-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-config",
|
||||||
|
"jay-utils",
|
||||||
|
"linearize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-udmabuf"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-allocator",
|
||||||
|
"jay-formats",
|
||||||
|
"jay-utils",
|
||||||
|
"jay-video-types",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-units"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"arrayvec",
|
||||||
|
"bstr",
|
||||||
|
"cfg-if",
|
||||||
|
"isnt 0.2.0",
|
||||||
|
"jay-config",
|
||||||
|
"linearize",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"rand 0.10.0",
|
||||||
|
"serde",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-video-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"jay-formats",
|
||||||
|
"jay-utils",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-wheel"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-time",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-wire-buf"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-time",
|
||||||
|
"jay-units",
|
||||||
|
"jay-utils",
|
||||||
|
"jay-wire-types",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-wire-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-xcon"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"jay-bufio",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
102
Cargo.toml
102
Cargo.toml
|
|
@ -13,7 +13,56 @@ name = "jay"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["jay-config", "toml-config", "algorithms", "toml-spec", "wire-to-xml", "xml-to-wire"]
|
resolver = "3"
|
||||||
|
members = [
|
||||||
|
"crates/jay-config",
|
||||||
|
"crates/jay-config-schema",
|
||||||
|
"crates/geometry",
|
||||||
|
"crates/layout-animation",
|
||||||
|
"crates/formats",
|
||||||
|
"crates/edid",
|
||||||
|
"crates/units",
|
||||||
|
"crates/utils",
|
||||||
|
"crates/criteria",
|
||||||
|
"crates/cmm",
|
||||||
|
"crates/time",
|
||||||
|
"crates/tracy",
|
||||||
|
"crates/async-engine",
|
||||||
|
"crates/io-uring",
|
||||||
|
"crates/bufio",
|
||||||
|
"crates/dbus-core",
|
||||||
|
"crates/xcon",
|
||||||
|
"crates/wire-types",
|
||||||
|
"crates/wire-buf",
|
||||||
|
"crates/tree-types",
|
||||||
|
"crates/eventfd-cache",
|
||||||
|
"crates/wheel",
|
||||||
|
"crates/cpu-worker",
|
||||||
|
"crates/sighand",
|
||||||
|
"crates/pr-caps",
|
||||||
|
"crates/bugs",
|
||||||
|
"crates/logger",
|
||||||
|
"crates/video-types",
|
||||||
|
"crates/output-types",
|
||||||
|
"crates/input-types",
|
||||||
|
"crates/keyboard",
|
||||||
|
"crates/gfx-types",
|
||||||
|
"crates/theme",
|
||||||
|
"crates/clientmem",
|
||||||
|
"crates/allocator",
|
||||||
|
"crates/output-schedule",
|
||||||
|
"crates/drm-feedback",
|
||||||
|
"crates/udmabuf",
|
||||||
|
"crates/damage",
|
||||||
|
"crates/pango",
|
||||||
|
"crates/libinput",
|
||||||
|
"crates/toml-config",
|
||||||
|
"crates/toml-parser",
|
||||||
|
"crates/algorithms",
|
||||||
|
"crates/toml-spec",
|
||||||
|
"crates/wire-to-xml",
|
||||||
|
"crates/xml-to-wire",
|
||||||
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
@ -23,9 +72,48 @@ debug = "full"
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jay-config = { version = "1.10.0", path = "jay-config" }
|
jay-config = { version = "1.10.0", path = "crates/jay-config" }
|
||||||
jay-toml-config = { version = "0.12.0", path = "toml-config" }
|
jay-toml-config = { version = "0.12.0", path = "crates/toml-config" }
|
||||||
jay-algorithms = { version = "0.4.0", path = "algorithms" }
|
jay-algorithms = { version = "0.4.0", path = "crates/algorithms" }
|
||||||
|
jay-geometry = { version = "0.1.0", path = "crates/geometry" }
|
||||||
|
jay-layout-animation = { version = "0.1.0", path = "crates/layout-animation" }
|
||||||
|
jay-formats = { version = "0.1.0", path = "crates/formats" }
|
||||||
|
jay-edid = { version = "0.1.0", path = "crates/edid" }
|
||||||
|
jay-units = { version = "0.1.0", path = "crates/units" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "crates/utils" }
|
||||||
|
jay-criteria = { version = "0.1.0", path = "crates/criteria" }
|
||||||
|
jay-cmm = { version = "0.1.0", path = "crates/cmm" }
|
||||||
|
jay-time = { version = "0.1.0", path = "crates/time" }
|
||||||
|
jay-tracy = { version = "0.1.0", path = "crates/tracy" }
|
||||||
|
jay-async-engine = { version = "0.1.0", path = "crates/async-engine" }
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "crates/io-uring" }
|
||||||
|
jay-bufio = { version = "0.1.0", path = "crates/bufio" }
|
||||||
|
jay-dbus-core = { version = "0.1.0", path = "crates/dbus-core" }
|
||||||
|
jay-xcon = { version = "0.1.0", path = "crates/xcon" }
|
||||||
|
jay-wire-types = { version = "0.1.0", path = "crates/wire-types" }
|
||||||
|
jay-wire-buf = { version = "0.1.0", path = "crates/wire-buf" }
|
||||||
|
jay-tree-types = { version = "0.1.0", path = "crates/tree-types" }
|
||||||
|
jay-eventfd-cache = { version = "0.1.0", path = "crates/eventfd-cache" }
|
||||||
|
jay-wheel = { version = "0.1.0", path = "crates/wheel" }
|
||||||
|
jay-cpu-worker = { version = "0.1.0", path = "crates/cpu-worker" }
|
||||||
|
jay-sighand = { version = "0.1.0", path = "crates/sighand" }
|
||||||
|
jay-pr-caps = { version = "0.1.0", path = "crates/pr-caps" }
|
||||||
|
jay-bugs = { version = "0.1.0", path = "crates/bugs" }
|
||||||
|
jay-logger = { version = "0.1.0", path = "crates/logger" }
|
||||||
|
jay-video-types = { version = "0.1.0", path = "crates/video-types" }
|
||||||
|
jay-output-types = { version = "0.1.0", path = "crates/output-types" }
|
||||||
|
jay-input-types = { version = "0.1.0", path = "crates/input-types" }
|
||||||
|
jay-keyboard = { version = "0.1.0", path = "crates/keyboard" }
|
||||||
|
jay-gfx-types = { version = "0.1.0", path = "crates/gfx-types" }
|
||||||
|
jay-theme = { version = "0.1.0", path = "crates/theme" }
|
||||||
|
jay-clientmem = { version = "0.1.0", path = "crates/clientmem" }
|
||||||
|
jay-allocator = { version = "0.1.0", path = "crates/allocator" }
|
||||||
|
jay-output-schedule = { version = "0.1.0", path = "crates/output-schedule" }
|
||||||
|
jay-drm-feedback = { version = "0.1.0", path = "crates/drm-feedback" }
|
||||||
|
jay-udmabuf = { version = "0.1.0", path = "crates/udmabuf" }
|
||||||
|
jay-damage = { version = "0.1.0", path = "crates/damage" }
|
||||||
|
jay-pango = { version = "0.1.0", path = "crates/pango" }
|
||||||
|
jay-libinput = { version = "0.1.0", path = "crates/libinput" }
|
||||||
|
|
||||||
uapi = "0.2.13"
|
uapi = "0.2.13"
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
|
|
@ -58,8 +146,6 @@ serde = { version = "1.0.196", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
linearize = { version = "0.1.3", features = ["derive"] }
|
||||||
png = "0.18.0"
|
png = "0.18.0"
|
||||||
rustc-demangle = { version = "0.1.24", optional = true }
|
|
||||||
tracy-client-sys = { version = "0.24.1", features = ["ondemand", "manual-lifetime", "debuginfod", "demangle"], optional = true }
|
|
||||||
kbvm = { version = "0.1.6", features = ["compose"] }
|
kbvm = { version = "0.1.6", features = ["compose"] }
|
||||||
tiny-skia = { version = "0.12.0", default-features = false, features = ["std"] }
|
tiny-skia = { version = "0.12.0", default-features = false, features = ["std"] }
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
|
@ -90,5 +176,5 @@ opt-level = 3
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
rc_tracking = []
|
rc_tracking = []
|
||||||
it = []
|
it = ["jay-async-engine/it", "jay-cpu-worker/it"]
|
||||||
tracy = ["dep:tracy-client-sys", "dep:rustc-demangle"]
|
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy", "jay-clientmem/tracy"]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
[](http://crates.io/crates/jay-compositor)
|
[](http://crates.io/crates/jay-compositor)
|
||||||
|
|
||||||
Jay is a Wayland compositor for Linux with an i3-like tiling layout,
|
Jay is a Wayland compositor for Linux with an i3-like tiling layout,
|
||||||
Vulkan and OpenGL rendering, multi-GPU support, screen sharing, and more.
|
Vulkan and OpenGL rendering, multi-GPU support, and more.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,14 @@ The table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
|
||||||
|
|
||||||
| File | What it tells you |
|
| File | What it tells you |
|
||||||
|------|-------------------|
|
|------|-------------------|
|
||||||
| `toml-spec/spec/spec.yaml` | **Canonical** TOML config spec: every key, action, match criterion, type |
|
| `crates/toml-spec/spec/spec.yaml` | **Canonical** TOML config spec: every key, action, match criterion, type |
|
||||||
| `toml-config/src/default-config.toml` | Built-in default config (keybindings, startup actions) |
|
| `crates/toml-config/src/default-config.toml` | Built-in default config (keybindings, startup actions) |
|
||||||
| `toml-config/src/config/parsers/action.rs` | Action parser — see which `type` strings are accepted |
|
| `crates/toml-config/src/config/parsers/action.rs` | Action parser — see which `type` strings are accepted |
|
||||||
| `toml-config/src/lib.rs` | Action dispatch — `window_or_seat!` macro shows which actions work in window rules |
|
| `crates/toml-config/src/lib.rs` | Action dispatch — `window_or_seat!` macro shows which actions work in window rules |
|
||||||
| `src/config/handler.rs` | Config handler; `update_capabilities` shows capability replacement semantics |
|
| `src/config/handler.rs` | Config handler; `update_capabilities` shows capability replacement semantics |
|
||||||
| `src/cli/*.rs` | CLI subcommands (clap definitions) |
|
| `src/cli/*.rs` | CLI subcommands (clap definitions) |
|
||||||
| `src/control_center/cc_*.rs` | Control center pane implementations (verify field names/ordering here) |
|
| `src/control_center/cc_*.rs` | Control center pane implementations (verify field names/ordering here) |
|
||||||
| `toml-config/src/config/parsers/exec.rs` | Exec parser (string, array, or table forms) |
|
| `crates/toml-config/src/config/parsers/exec.rs` | Exec parser (string, array, or table forms) |
|
||||||
|
|
||||||
### Known spec.yaml bugs
|
### Known spec.yaml bugs
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@ The table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
|
||||||
- **Definition lists** for two-column term/description. Tables only for 3+ data columns.
|
- **Definition lists** for two-column term/description. Tables only for 3+ data columns.
|
||||||
- **TOML formatting:** multiline with trailing commas, 4-space indent.
|
- **TOML formatting:** multiline with trailing commas, 4-space indent.
|
||||||
- **Examples:** practical, not abstract. Link to
|
- **Examples:** practical, not abstract. Link to
|
||||||
[spec.generated.md](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md)
|
[spec.generated.md](https://github.com/mahkoh/jay/blob/master/crates/toml-spec/spec/spec.generated.md)
|
||||||
for exhaustive listings.
|
for exhaustive listings.
|
||||||
- **Control center docs:** verify field names, ordering, and conditional
|
- **Control center docs:** verify field names, ordering, and conditional
|
||||||
visibility against `cc_*.rs` source files. Labels must match exactly.
|
visibility against `cc_*.rs` source files. Labels must match exactly.
|
||||||
|
|
@ -91,10 +91,10 @@ The table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
|
||||||
### Documenting a new action
|
### Documenting a new action
|
||||||
|
|
||||||
1. Read `git diff` for the commit introducing the action. Key files:
|
1. Read `git diff` for the commit introducing the action. Key files:
|
||||||
- `toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
|
- `crates/toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
|
||||||
- `toml-config/src/config/parsers/action.rs` — parser (field names, types, defaults)
|
- `crates/toml-config/src/config/parsers/action.rs` — parser (field names, types, defaults)
|
||||||
- `toml-config/src/lib.rs` — dispatch (check if `window_or_seat!` is used)
|
- `crates/toml-config/src/lib.rs` — dispatch (check if `window_or_seat!` is used)
|
||||||
- `jay-config/src/input.rs` and/or `jay-config/src/window.rs` — Rust API
|
- `crates/jay-config/src/input.rs` and/or `crates/jay-config/src/window.rs` — Rust API
|
||||||
|
|
||||||
2. Edit `book/src/configuration/shortcuts.md`:
|
2. Edit `book/src/configuration/shortcuts.md`:
|
||||||
- **Simple actions** (no fields): add to the appropriate list in the
|
- **Simple actions** (no fields): add to the appropriate list in the
|
||||||
|
|
@ -110,7 +110,7 @@ The table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
|
||||||
|
|
||||||
### Documenting a new config field
|
### Documenting a new config field
|
||||||
|
|
||||||
1. Read `toml-spec/spec/spec.yaml` for the field definition.
|
1. Read `crates/toml-spec/spec/spec.yaml` for the field definition.
|
||||||
2. Identify which book chapter covers that config section (see table above).
|
2. Identify which book chapter covers that config section (see table above).
|
||||||
3. Add the field with a definition-list entry or example, matching the
|
3. Add the field with a definition-list entry or example, matching the
|
||||||
existing style of that chapter.
|
existing style of that chapter.
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@
|
||||||
- [Mouse Interactions](mouse.md)
|
- [Mouse Interactions](mouse.md)
|
||||||
- [Input Modes](input-modes.md)
|
- [Input Modes](input-modes.md)
|
||||||
- [Window & Client Rules](window-rules.md)
|
- [Window & Client Rules](window-rules.md)
|
||||||
- [Screen Sharing](screen-sharing.md)
|
|
||||||
- [HDR & Color Management](hdr.md)
|
- [HDR & Color Management](hdr.md)
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
|
|
|
||||||
|
|
@ -674,18 +674,6 @@ Show color management status:
|
||||||
|
|
||||||
## Other Commands
|
## Other Commands
|
||||||
|
|
||||||
### `jay portal`
|
|
||||||
|
|
||||||
Run the Jay desktop portal (provides screen sharing and other XDG desktop
|
|
||||||
portal interfaces):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay portal
|
|
||||||
```
|
|
||||||
|
|
||||||
Normally the portal is started automatically. This command is for running it
|
|
||||||
manually or debugging.
|
|
||||||
|
|
||||||
### `jay seat-test`
|
### `jay seat-test`
|
||||||
|
|
||||||
Test input events from a seat. Prints all keyboard, pointer, touch, gesture,
|
Test input events from a seat. Prints all keyboard, pointer, touch, gesture,
|
||||||
|
|
@ -697,24 +685,6 @@ tablet, and switch events to stdout:
|
||||||
~$ jay seat-test -a # test all seats simultaneously
|
~$ jay seat-test -a # test all seats simultaneously
|
||||||
```
|
```
|
||||||
|
|
||||||
### `jay run-privileged`
|
|
||||||
|
|
||||||
Run a program with access to a privileged Wayland socket:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay run-privileged my-program --arg1
|
|
||||||
```
|
|
||||||
|
|
||||||
### `jay run-tagged`
|
|
||||||
|
|
||||||
Run a program with a tagged Wayland connection. All Wayland connections from the
|
|
||||||
spawned process tree will carry the specified tag, which can be matched in
|
|
||||||
[client rules](window-rules.md):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay run-tagged my-tag firefox
|
|
||||||
```
|
|
||||||
|
|
||||||
### `jay generate-completion`
|
### `jay generate-completion`
|
||||||
|
|
||||||
Generate shell completion scripts:
|
Generate shell completion scripts:
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,10 @@ on-idle = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> Screen lockers that use the Wayland session lock protocol (like swaylock)
|
|
||||||
> need `privileged = true` in the exec configuration. This grants the process
|
|
||||||
> the necessary permissions to lock the session.
|
|
||||||
|
|
||||||
You can also combine multiple actions:
|
You can also combine multiple actions:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
@ -80,7 +74,6 @@ on-idle = [
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type = "exec", exec = ["notify-send", "System locked"] },
|
{ type = "exec", exec = ["notify-send", "System locked"] },
|
||||||
|
|
@ -100,7 +93,6 @@ on-idle = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,8 @@ the color management protocol.
|
||||||
## Libei
|
## Libei
|
||||||
|
|
||||||
[libei](https://gitlab.freedesktop.org/libinput/libei) allows applications to
|
[libei](https://gitlab.freedesktop.org/libinput/libei) allows applications to
|
||||||
emulate input events. By default, applications can only access libei through
|
emulate input events. Setting `enable-socket` exposes an unauthenticated socket
|
||||||
the portal (which prompts the user for permission). Setting `enable-socket`
|
that any application can use.
|
||||||
exposes an unauthenticated socket that any application can use without a prompt.
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
libei.enable-socket = false # default
|
libei.enable-socket = false # default
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,6 @@ alt-shift-r = "reload-config-toml"
|
||||||
(the next pressed key identifies the mark). See [Marks](#marks) below.
|
(the next pressed key identifies the mark). See [Marks](#marks) below.
|
||||||
- `enable-window-management`, `disable-window-management` -- programmatically
|
- `enable-window-management`, `disable-window-management` -- programmatically
|
||||||
enable or disable [window management mode](../floating.md#window-management-mode)
|
enable or disable [window management mode](../floating.md#window-management-mode)
|
||||||
- `reload-config-so` -- reload the shared-library configuration (`config.so`)
|
|
||||||
|
|
||||||
See the [specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the full list of simple
|
See the [specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the full list of simple
|
||||||
actions.
|
actions.
|
||||||
|
|
@ -309,7 +308,6 @@ alt-s = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
shell = "grim - | wl-copy",
|
shell = "grim - | wl-copy",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -328,12 +326,6 @@ Table fields:
|
||||||
`env`
|
`env`
|
||||||
: Per-process environment variables
|
: Per-process environment variables
|
||||||
|
|
||||||
`privileged`
|
|
||||||
: If `true`, grants access to privileged Wayland protocols (default: `false`)
|
|
||||||
|
|
||||||
`tag`
|
|
||||||
: Tag to apply to all Wayland connections spawned by this process
|
|
||||||
|
|
||||||
### Practical examples
|
### Practical examples
|
||||||
|
|
||||||
Volume control with `pactl`:
|
Volume control with `pactl`:
|
||||||
|
|
@ -362,7 +354,6 @@ Print = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
shell = "grim - | wl-copy",
|
shell = "grim - | wl-copy",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -63,15 +63,10 @@ on-idle = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Screen lockers need `privileged = true` to access the privileged Wayland
|
|
||||||
> protocols required for locking the session.
|
|
||||||
|
|
||||||
You can combine idle with a grace period. The idle timeout and grace period are
|
You can combine idle with a grace period. The idle timeout and grace period are
|
||||||
configured separately in the `[idle]` section (see [Idle & Screen
|
configured separately in the `[idle]` section (see [Idle & Screen
|
||||||
Locking](idle.md)):
|
Locking](idle.md)):
|
||||||
|
|
@ -83,7 +78,6 @@ on-idle = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -97,7 +91,6 @@ on-idle = [
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
privileged = true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ The available color keys in the `[theme]` table are:
|
||||||
|
|
||||||
"Focused-inactive" refers to a window that was most recently focused in its
|
"Focused-inactive" refers to a window that was most recently focused in its
|
||||||
container but whose container is not the active one. The "captured" colors apply
|
container but whose container is not the active one. The "captured" colors apply
|
||||||
when a window is being recorded (e.g. via screen sharing).
|
when a window is being captured.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,5 +87,5 @@ Xwayland client itself:
|
||||||
```toml
|
```toml
|
||||||
[[clients]]
|
[[clients]]
|
||||||
match.is-xwayland = true
|
match.is-xwayland = true
|
||||||
# ... grant capabilities, etc.
|
# ... configure client-specific behavior
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,7 @@ Commands:
|
||||||
unlock Unlocks the compositor
|
unlock Unlocks the compositor
|
||||||
screenshot Take a screenshot
|
screenshot Take a screenshot
|
||||||
idle Inspect/modify the idle (screensaver) settings
|
idle Inspect/modify the idle (screensaver) settings
|
||||||
run-privileged Run a privileged program
|
|
||||||
run-tagged Run a program with a connection tag
|
|
||||||
seat-test Tests the events produced by a seat
|
seat-test Tests the events produced by a seat
|
||||||
portal Run the desktop portal
|
|
||||||
randr Inspect/modify graphics card and connector settings
|
randr Inspect/modify graphics card and connector settings
|
||||||
input Inspect/modify input settings
|
input Inspect/modify input settings
|
||||||
xwayland Inspect/modify xwayland settings
|
xwayland Inspect/modify xwayland settings
|
||||||
|
|
@ -101,17 +98,6 @@ runtime.
|
||||||
|
|
||||||
See [GPUs](configuration/gpu.md) for details.
|
See [GPUs](configuration/gpu.md) for details.
|
||||||
|
|
||||||
## Screen Sharing
|
|
||||||
|
|
||||||
Jay supports screen sharing via xdg-desktop-portal. Three capture modes are
|
|
||||||
available:
|
|
||||||
|
|
||||||
- **Window capture** -- share a single window.
|
|
||||||
- **Output capture** -- share an entire monitor.
|
|
||||||
- **Workspace capture** -- like output capture, but only one workspace is shown.
|
|
||||||
|
|
||||||
See [Screen Sharing](screen-sharing.md) for setup instructions.
|
|
||||||
|
|
||||||
## Screen Locking
|
## Screen Locking
|
||||||
|
|
||||||
Jay can automatically lock your screen and disable outputs after inactivity.
|
Jay can automatically lock your screen and disable outputs after inactivity.
|
||||||
|
|
@ -154,20 +140,6 @@ Jay supports running X11 applications seamlessly through Xwayland. See
|
||||||
Jay supports clipboard managers via the `zwlr_data_control_manager_v1` and
|
Jay supports clipboard managers via the `zwlr_data_control_manager_v1` and
|
||||||
`ext_data_control_manager_v1` protocols.
|
`ext_data_control_manager_v1` protocols.
|
||||||
|
|
||||||
## Privilege Separation
|
|
||||||
|
|
||||||
Jay splits protocols into unprivileged and privileged protocols. By default,
|
|
||||||
applications only have access to unprivileged protocols. This means that tools
|
|
||||||
like screen lockers, status bars, and clipboard managers need to be explicitly
|
|
||||||
granted access.
|
|
||||||
|
|
||||||
Jay provides several ways to grant privileges, from giving a program full
|
|
||||||
access to all privileged protocols down to granting individual capabilities to
|
|
||||||
specific tagged processes. See
|
|
||||||
[Granting Privileges](window-rules.md#granting-privileges) for a detailed
|
|
||||||
guide and the [Protocol Support](#protocol-support) section below for the full
|
|
||||||
list of protocols and their privilege requirements.
|
|
||||||
|
|
||||||
## Push to Talk
|
## Push to Talk
|
||||||
|
|
||||||
Jay's shortcut system allows you to execute an action when a key is pressed and
|
Jay's shortcut system allows you to execute an action when a key is pressed and
|
||||||
|
|
@ -252,7 +224,6 @@ granted access. See
|
||||||
| wp_linux_drm_syncobj_manager_v1 | 1 | |
|
| wp_linux_drm_syncobj_manager_v1 | 1 | |
|
||||||
| wp_pointer_warp_v1 | 1 | |
|
| wp_pointer_warp_v1 | 1 | |
|
||||||
| wp_presentation | 2 | |
|
| wp_presentation | 2 | |
|
||||||
| wp_security_context_manager_v1 | 1 | |
|
|
||||||
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
||||||
| wp_tearing_control_manager_v1 | 1 | |
|
| wp_tearing_control_manager_v1 | 1 | |
|
||||||
| wp_viewporter | 1 | |
|
| wp_viewporter | 1 | |
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ For Vulkan, you also need the driver for your GPU:
|
||||||
|
|
||||||
- **Linux 6.7 or later** -- required for explicit sync (needed for Nvidia GPUs).
|
- **Linux 6.7 or later** -- required for explicit sync (needed for Nvidia GPUs).
|
||||||
- **Xwayland** -- required for running X11 applications.
|
- **Xwayland** -- required for running X11 applications.
|
||||||
- **PipeWire** -- required for screen sharing.
|
|
||||||
- **logind** (part of systemd) -- required when running Jay from a virtual terminal or display manager.
|
- **logind** (part of systemd) -- required when running Jay from a virtual terminal or display manager.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
@ -129,14 +128,7 @@ retains `CAP_SYS_NICE` solely for creating elevated Vulkan queues later.
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> You need to re-run the `setcap` command each time you update the Jay binary.
|
> You need to re-run the `setcap` command each time you update the Jay binary.
|
||||||
|
|
||||||
### SCHED_RR and config.so
|
### SCHED_RR
|
||||||
|
|
||||||
`SCHED_RR` and `config.so` are mutually exclusive: running untrusted code at
|
|
||||||
real-time priority would be a security risk. Jay enforces this as follows:
|
|
||||||
|
|
||||||
- If `config.so` exists in the config directory, Jay skips the `SCHED_RR`
|
|
||||||
elevation (elevated Vulkan queues are still created).
|
|
||||||
- If Jay has already elevated to `SCHED_RR`, it refuses to load `config.so`.
|
|
||||||
|
|
||||||
You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
|
You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
|
||||||
|
|
||||||
|
|
@ -144,11 +136,7 @@ You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
|
||||||
~$ JAY_NO_REALTIME=1 jay run
|
~$ JAY_NO_REALTIME=1 jay run
|
||||||
```
|
```
|
||||||
|
|
||||||
This still allows elevated Vulkan queues and does not affect `config.so`
|
This still allows elevated Vulkan queues.
|
||||||
loading.
|
|
||||||
|
|
||||||
The mutual exclusion can be overridden at compile time by building Jay with
|
|
||||||
`JAY_ALLOW_REALTIME_CONFIG_SO=1`.
|
|
||||||
|
|
||||||
## Recommended Applications
|
## Recommended Applications
|
||||||
|
|
||||||
|
|
@ -156,7 +144,6 @@ The following applications work well with Jay:
|
||||||
|
|
||||||
- **[Alacritty](https://alacritty.org/)** -- the default terminal emulator in the built-in configuration.
|
- **[Alacritty](https://alacritty.org/)** -- the default terminal emulator in the built-in configuration.
|
||||||
- **[bemenu](https://github.com/Cloudef/bemenu)** -- the default application launcher in the built-in configuration.
|
- **[bemenu](https://github.com/Cloudef/bemenu)** -- the default application launcher in the built-in configuration.
|
||||||
- **[xdg-desktop-portal-gtk4](https://github.com/mahkoh/xdg-desktop-portal-gtk4)** -- a file-picker portal with thumbnail support. Used automatically when installed.
|
|
||||||
- **[wl-tray-bridge](https://github.com/mahkoh/wl-tray-bridge)** -- shows D-Bus StatusNotifierItem applications as tray icons.
|
- **[wl-tray-bridge](https://github.com/mahkoh/wl-tray-bridge)** -- shows D-Bus StatusNotifierItem applications as tray icons.
|
||||||
- **[mako](https://github.com/emersion/mako)** -- a notification daemon. Launched automatically by the default configuration.
|
- **[mako](https://github.com/emersion/mako)** -- a notification daemon. Launched automatically by the default configuration.
|
||||||
- **[window-to-tray](https://github.com/mahkoh/wl-proxy/tree/master/apps/window-to-tray)** -- run most Wayland applications as tray applications (e.g. `window-to-tray pavucontrol-qt`).
|
- **[window-to-tray](https://github.com/mahkoh/wl-proxy/tree/master/apps/window-to-tray)** -- run most Wayland applications as tray applications (e.g. `window-to-tray pavucontrol-qt`).
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,11 @@
|
||||||
|
|
||||||
Jay is a Wayland compositor for Linux with an i3-inspired tiling layout. It
|
Jay is a Wayland compositor for Linux with an i3-inspired tiling layout. It
|
||||||
supports Vulkan and OpenGL rendering, multi-GPU setups, fractional scaling,
|
supports Vulkan and OpenGL rendering, multi-GPU setups, fractional scaling,
|
||||||
variable refresh rate (VRR), tearing presentation, HDR, and screen sharing via
|
variable refresh rate (VRR), tearing presentation, and HDR. X11 applications
|
||||||
xdg-desktop-portal. X11 applications are supported through Xwayland.
|
are supported through Xwayland.
|
||||||
|
|
||||||
Jay is configured through a declarative TOML file, with an optional advanced
|
Jay is configured through a declarative TOML file. A comprehensive command-line
|
||||||
mode that uses a shared library for programmatic control. A comprehensive
|
interface makes scripting and automation straightforward.
|
||||||
command-line interface makes scripting and automation straightforward.
|
|
||||||
|
|
||||||
See the [Features](features.md) chapter for a comprehensive overview of what
|
See the [Features](features.md) chapter for a comprehensive overview of what
|
||||||
Jay can do, or jump straight to [Installation](installation.md) to get started.
|
Jay can do, or jump straight to [Installation](installation.md) to get started.
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ This is especially useful for:
|
||||||
|
|
||||||
## Other
|
## Other
|
||||||
|
|
||||||
**Toplevel selection.** Some actions (like screen sharing) ask you to select a
|
**Toplevel selection.** Some actions ask you to select a window, indicated by a
|
||||||
window, indicated by a purple overlay. During this selection, right-click a
|
purple overlay. During this selection, right-click a
|
||||||
tile's title to select the entire container instead of an individual tile.
|
tile's title to select the entire container instead of an individual tile.
|
||||||
|
|
||||||
**Canceling interactions.** Press `Escape` to cancel any in-progress mouse
|
**Canceling interactions.** Press `Escape` to cancel any in-progress mouse
|
||||||
|
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
# Screen Sharing
|
|
||||||
|
|
||||||
Jay supports screen sharing via
|
|
||||||
[xdg-desktop-portal](https://github.com/flatpak/xdg-desktop-portal). Three
|
|
||||||
capture types are available:
|
|
||||||
|
|
||||||
- **Window capture** -- share a single window.
|
|
||||||
- **Output capture** -- share an entire monitor.
|
|
||||||
- **Workspace capture** -- like output capture, but only a single workspace is
|
|
||||||
shown.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
[PipeWire](https://pipewire.org/) must be installed and running. Verify with:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ systemctl --user status pipewire
|
|
||||||
```
|
|
||||||
|
|
||||||
## Portal Setup
|
|
||||||
|
|
||||||
Jay implements its own portal backend for the `ScreenCast` and `RemoteDesktop`
|
|
||||||
interfaces. Two configuration files must be installed so that
|
|
||||||
`xdg-desktop-portal` knows to use Jay's backend.
|
|
||||||
|
|
||||||
### If the Repository is Checked Out
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ sudo cp etc/jay.portal /usr/share/xdg-desktop-portal/portals/jay.portal
|
|
||||||
~$ sudo cp etc/jay-portals.conf /usr/share/xdg-desktop-portal/jay-portals.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
### If Installed via cargo install
|
|
||||||
|
|
||||||
Create the files manually:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ sudo tee /usr/share/xdg-desktop-portal/portals/jay.portal > /dev/null << 'EOF'
|
|
||||||
[portal]
|
|
||||||
DBusName=org.freedesktop.impl.portal.desktop.jay
|
|
||||||
Interfaces=org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ sudo tee /usr/share/xdg-desktop-portal/jay-portals.conf > /dev/null << 'EOF'
|
|
||||||
[preferred]
|
|
||||||
default=gtk
|
|
||||||
org.freedesktop.impl.portal.ScreenCast=jay
|
|
||||||
org.freedesktop.impl.portal.RemoteDesktop=jay
|
|
||||||
org.freedesktop.impl.portal.Inhibit=none
|
|
||||||
org.freedesktop.impl.portal.FileChooser=gtk4
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
### Restart the Portal
|
|
||||||
|
|
||||||
After installing the files, restart the portal service:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ systemctl --user restart xdg-desktop-portal
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### workspace-capture
|
|
||||||
|
|
||||||
The top-level `workspace-capture` setting controls whether newly created
|
|
||||||
workspaces can be captured via workspace capture. The default is `true`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
workspace-capture = false
|
|
||||||
```
|
|
||||||
|
|
||||||
Set this to `false` if you want to prevent workspace-level capture by default.
|
|
||||||
|
|
||||||
### Capture Indicator Colors
|
|
||||||
|
|
||||||
When a window is being recorded, its title bar color changes to make the
|
|
||||||
capture visually obvious. You can customize these colors in the `[theme]`
|
|
||||||
table:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[theme]
|
|
||||||
captured-focused-title-bg-color = "#900000"
|
|
||||||
captured-unfocused-title-bg-color = "#5f0000"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `captured-focused-title-bg-color` -- background color of focused title bars
|
|
||||||
that are being recorded.
|
|
||||||
- `captured-unfocused-title-bg-color` -- background color of unfocused title
|
|
||||||
bars that are being recorded.
|
|
||||||
|
|
||||||
## The jay portal Command
|
|
||||||
|
|
||||||
Jay's portal backend is normally started automatically when a screen-sharing
|
|
||||||
request comes in via D-Bus activation. If you need to start it manually for
|
|
||||||
debugging purposes:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay portal
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
If screen sharing does not work:
|
|
||||||
|
|
||||||
1. Verify PipeWire is running: `systemctl --user status pipewire`
|
|
||||||
2. Verify the portal files are installed in `/usr/share/xdg-desktop-portal/`.
|
|
||||||
3. Restart the portal: `systemctl --user restart xdg-desktop-portal`
|
|
||||||
4. Check the Jay log for errors: `jay log`
|
|
||||||
|
|
@ -54,57 +54,6 @@ bindings.
|
||||||
> when any config file exists. Always use `jay config init` to start with a
|
> when any config file exists. Always use `jay config init` to start with a
|
||||||
> working configuration.
|
> working configuration.
|
||||||
|
|
||||||
## Application doesn't have access to a protocol
|
|
||||||
|
|
||||||
Jay splits Wayland protocols into unprivileged and privileged. By default,
|
|
||||||
applications only have access to unprivileged protocols. If a program like a
|
|
||||||
screen locker, status bar, clipboard manager, or screen-capture tool is not
|
|
||||||
working, it likely needs access to one or more privileged protocols.
|
|
||||||
|
|
||||||
Common symptoms include:
|
|
||||||
|
|
||||||
- **swaylock** does nothing or fails to lock the screen (needs `session-lock`).
|
|
||||||
- **waybar** or **i3bar** shows no workspace information (needs
|
|
||||||
`foreign-toplevel-list`).
|
|
||||||
- **wl-copy** / **cliphist** cannot access the clipboard (needs
|
|
||||||
`data-control`).
|
|
||||||
- **grim** or **slurp** cannot capture the screen (needs `screencopy`).
|
|
||||||
|
|
||||||
**Quick fix -- grant all privileges:**
|
|
||||||
|
|
||||||
The simplest approach is to launch the program with full access to all
|
|
||||||
privileged protocols. In your config, set `privileged = true` in the exec
|
|
||||||
action:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
on-idle = {
|
|
||||||
type = "exec",
|
|
||||||
exec = {
|
|
||||||
prog = "swaylock",
|
|
||||||
privileged = true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Or from the command line:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay run-privileged waybar
|
|
||||||
```
|
|
||||||
|
|
||||||
**Better fix -- grant only the capabilities needed:**
|
|
||||||
|
|
||||||
Use a client rule to grant specific capabilities:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[clients]]
|
|
||||||
match.comm = "waybar"
|
|
||||||
capabilities = ["layer-shell", "foreign-toplevel-list"]
|
|
||||||
```
|
|
||||||
|
|
||||||
See [Granting Privileges](window-rules.md#granting-privileges) for the full
|
|
||||||
list of capabilities and more advanced approaches using connection tags.
|
|
||||||
|
|
||||||
## Wrong keyboard layout
|
## Wrong keyboard layout
|
||||||
|
|
||||||
The default keyboard layout is US QWERTY. To change it:
|
The default keyboard layout is US QWERTY. To change it:
|
||||||
|
|
@ -132,45 +81,6 @@ layout = "de"
|
||||||
This takes effect immediately but does not persist across restarts unless
|
This takes effect immediately but does not persist across restarts unless
|
||||||
configured in the config file.
|
configured in the config file.
|
||||||
|
|
||||||
## Screen sharing doesn't work
|
|
||||||
|
|
||||||
Screen sharing requires PipeWire and the Jay desktop portal.
|
|
||||||
|
|
||||||
**1. Check that PipeWire is running:**
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ systemctl --user status pipewire
|
|
||||||
```
|
|
||||||
|
|
||||||
If it is not running, start it:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ systemctl --user start pipewire
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Check that the portal files are installed:**
|
|
||||||
|
|
||||||
Jay needs two files to be found by the XDG desktop portal framework:
|
|
||||||
|
|
||||||
- A portal definition file (e.g. `/usr/share/xdg-desktop-portal/portals/jay.portal`).
|
|
||||||
- A portal configuration file (e.g. `/usr/share/xdg-desktop-portal/jay-portals.conf`).
|
|
||||||
|
|
||||||
These files are included in the Jay repository under `etc/`. If you built Jay
|
|
||||||
from source and did not install them, copy them manually:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ sudo cp etc/jay.portal /usr/share/xdg-desktop-portal/portals/
|
|
||||||
~$ sudo cp etc/jay-portals.conf /usr/share/xdg-desktop-portal/
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. Restart the portal:**
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ systemctl --user restart xdg-desktop-portal
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [Screen Sharing](screen-sharing.md) chapter for more details.
|
|
||||||
|
|
||||||
## X11 applications don't work
|
## X11 applications don't work
|
||||||
|
|
||||||
Jay uses Xwayland to run X11 applications.
|
Jay uses Xwayland to run X11 applications.
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,6 @@ Each client rule can have the following fields:
|
||||||
`latch`
|
`latch`
|
||||||
: An action to run when a client stops matching.
|
: An action to run when a client stops matching.
|
||||||
|
|
||||||
`capabilities`
|
|
||||||
: Wayland protocol access granted to matching clients.
|
|
||||||
|
|
||||||
`sandbox-bounding-capabilities`
|
|
||||||
: Upper bounds for protocols available to child sandboxes.
|
|
||||||
|
|
||||||
### Client Match Criteria
|
### Client Match Criteria
|
||||||
|
|
||||||
All client match criteria are constant over the lifetime of a client. If no
|
All client match criteria are constant over the lifetime of a client. If no
|
||||||
|
|
@ -70,142 +64,6 @@ implicitly AND-combined.
|
||||||
`exe` / `exe-regex`
|
`exe` / `exe-regex`
|
||||||
: The client's `/proc/pid/exe` path.
|
: The client's `/proc/pid/exe` path.
|
||||||
|
|
||||||
`tag` / `tag-regex`
|
|
||||||
: The connection tag of the client.
|
|
||||||
|
|
||||||
### Granting Privileges
|
|
||||||
|
|
||||||
Jay splits Wayland protocols into unprivileged and privileged. By default,
|
|
||||||
applications only have access to unprivileged protocols. This means that tools
|
|
||||||
like screen lockers, status bars, screen-capture utilities, and clipboard
|
|
||||||
managers will not work unless you explicitly grant them the necessary
|
|
||||||
privileges.
|
|
||||||
|
|
||||||
See the [Protocol Support](features.md#protocol-support) table in the Features
|
|
||||||
chapter for the full list of protocols and whether they are privileged.
|
|
||||||
|
|
||||||
There are three ways to grant privileges, from simplest to most fine-grained.
|
|
||||||
|
|
||||||
#### 1. Grant all privileges via `privileged = true` (exec) or `jay run-privileged`
|
|
||||||
|
|
||||||
The simplest approach gives a program access to **all** privileged protocols.
|
|
||||||
This is appropriate for trusted tools like screen lockers where you don't want
|
|
||||||
to think about which specific protocols they need.
|
|
||||||
|
|
||||||
In the config, set `privileged = true` in the exec table:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
on-idle = {
|
|
||||||
type = "exec",
|
|
||||||
exec = {
|
|
||||||
prog = "swaylock",
|
|
||||||
privileged = true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
From the command line, use `jay run-privileged`:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay run-privileged waybar
|
|
||||||
```
|
|
||||||
|
|
||||||
Both methods connect the program to a privileged Wayland socket that grants
|
|
||||||
access to all privileged protocols.
|
|
||||||
|
|
||||||
#### 2. Grant capabilities via connection tags
|
|
||||||
|
|
||||||
Connection tags let you combine the CLI with client rules for precise control.
|
|
||||||
You tag a program at launch time, then write a client rule that matches
|
|
||||||
the tag and grants specific capabilities.
|
|
||||||
|
|
||||||
First, launch the program with a tag -- either from the command line:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ jay run-tagged bar waybar
|
|
||||||
```
|
|
||||||
|
|
||||||
Or from the config using the `tag` field in an exec action:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[shortcuts]
|
|
||||||
alt-w = {
|
|
||||||
type = "exec",
|
|
||||||
exec = {
|
|
||||||
prog = "waybar",
|
|
||||||
tag = "bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then write a client rule that matches the tag and grants capabilities:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[clients]]
|
|
||||||
match.tag = "bar"
|
|
||||||
capabilities = ["layer-shell", "foreign-toplevel-list"]
|
|
||||||
```
|
|
||||||
|
|
||||||
This way, only the specific instance you launched with the tag receives the
|
|
||||||
privileges -- other programs with the same binary name do not.
|
|
||||||
|
|
||||||
Available capability values: `none`, `all`, `data-control`,
|
|
||||||
`virtual-keyboard`, `foreign-toplevel-list`, `idle-notifier`, `session-lock`,
|
|
||||||
`layer-shell`, `screencopy`, `seat-manager`, `drm-lease`, `input-method`,
|
|
||||||
`workspace-manager`, `foreign-toplevel-manager`, `head-manager`,
|
|
||||||
`gamma-control-manager`, `virtual-pointer`.
|
|
||||||
|
|
||||||
**Default capabilities:** unsandboxed clients receive `layer-shell` and
|
|
||||||
`drm-lease`. Sandboxed clients receive only `drm-lease`. If any client rule
|
|
||||||
matches, its capabilities **replace** the defaults entirely. If multiple rules
|
|
||||||
match, their capabilities are unioned together, but the defaults are not
|
|
||||||
included unless a matching rule also grants them.
|
|
||||||
|
|
||||||
#### 3. Grant capabilities via client match rules
|
|
||||||
|
|
||||||
Client rules can also match programs by properties like their executable name
|
|
||||||
instead of a tag. This is convenient when you always want a given program to
|
|
||||||
have certain capabilities, regardless of how it was launched:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[clients]]
|
|
||||||
match.comm = "waybar"
|
|
||||||
capabilities = ["layer-shell", "foreign-toplevel-list"]
|
|
||||||
|
|
||||||
# Vim 9.2 uses the data-control protocol for seamless wayland integration.
|
|
||||||
[[clients]]
|
|
||||||
match.comm = "vim"
|
|
||||||
match.sandboxed = false
|
|
||||||
capabilities = "data-control"
|
|
||||||
|
|
||||||
# Older versions use wl-copy and wl-paste.
|
|
||||||
[[clients]]
|
|
||||||
match.any = [
|
|
||||||
{ comm = "wl-copy" },
|
|
||||||
{ comm = "wl-paste" },
|
|
||||||
]
|
|
||||||
match.sandboxed = false
|
|
||||||
capabilities = "data-control"
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Client match criteria like `comm`, `exe`, and `pid` are checked when a
|
|
||||||
> client connects. Any process with a matching name receives the specified
|
|
||||||
> capabilities. If you need to restrict privileges to programs you launch
|
|
||||||
> yourself, use connection tags (method 2) instead.
|
|
||||||
|
|
||||||
#### Bounding capabilities (sandboxes)
|
|
||||||
|
|
||||||
Capabilities can never exceed the client's **bounding capabilities**. Use
|
|
||||||
`sandbox-bounding-capabilities` on a client rule to set the upper bound for
|
|
||||||
protocols available to sandboxes created by that client:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[clients]]
|
|
||||||
match.comm = "flatpak-portal"
|
|
||||||
sandbox-bounding-capabilities = ["drm-lease", "layer-shell"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Window Rules
|
## Window Rules
|
||||||
|
|
||||||
Window rules operate on individual windows. They are defined with `[[windows]]`
|
Window rules operate on individual windows. They are defined with `[[windows]]`
|
||||||
|
|
@ -456,18 +314,6 @@ action = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Grant Protocol Access to a Trusted App
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[clients]]
|
|
||||||
match.comm = "swaylock"
|
|
||||||
capabilities = ["session-lock", "layer-shell"]
|
|
||||||
|
|
||||||
[[clients]]
|
|
||||||
match.comm = "waybar"
|
|
||||||
capabilities = ["layer-shell", "foreign-toplevel-list"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Suppress Focus Stealing for Chromium Screen-Share Windows
|
### Suppress Focus Stealing for Chromium Screen-Share Windows
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
|
||||||
|
|
@ -123,16 +123,15 @@ laptop.
|
||||||
|
|
||||||
## Workspace Capture
|
## Workspace Capture
|
||||||
|
|
||||||
By default, newly created workspaces can be captured for screen sharing. You
|
By default, newly created workspaces can be captured by capture clients. You can
|
||||||
can disable this globally:
|
disable this globally:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
workspace-capture = false
|
workspace-capture = false
|
||||||
```
|
```
|
||||||
|
|
||||||
When workspace capture is enabled, screen-sharing applications can share
|
When workspace capture is enabled, compositor-native capture clients may capture
|
||||||
individual workspaces (in addition to full outputs and individual windows). See
|
individual workspaces instead of whole outputs.
|
||||||
[Screen Sharing](screen-sharing.md) for more details.
|
|
||||||
|
|
||||||
## Matching Windows by Workspace
|
## Matching Windows by Workspace
|
||||||
|
|
||||||
|
|
|
||||||
129
build/enums.rs
129
build/enums.rs
|
|
@ -4,18 +4,27 @@ use {
|
||||||
std::{env, io::Write},
|
std::{env, io::Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[expect(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
#[macro_use]
|
macro_rules! cenum {
|
||||||
#[path = "../src/macros.rs"]
|
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
|
||||||
mod macros;
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct $name(pub i32);
|
||||||
|
|
||||||
#[path = "../src/libinput/consts.rs"]
|
impl $name {
|
||||||
mod libinput;
|
pub fn raw(self) -> i32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[path = "../src/pango/consts.rs"]
|
pub const $uc: &[i32] = &[$($val,)*];
|
||||||
mod pango;
|
|
||||||
|
|
||||||
#[path = "../src/fontconfig/consts.rs"]
|
$(
|
||||||
|
pub const $name2: $name = $name($val);
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[path = "fontconfig_consts.rs"]
|
||||||
mod fontconfig;
|
mod fontconfig;
|
||||||
|
|
||||||
fn get_target() -> repc::Target {
|
fn get_target() -> repc::Target {
|
||||||
|
|
@ -49,108 +58,6 @@ fn write_ty<W: Write>(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() -> anyhow::Result<()> {
|
pub fn main() -> anyhow::Result<()> {
|
||||||
let mut f = open("libinput_tys.rs")?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_LOG_PRIORITY,
|
|
||||||
"libinput_log_priority",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_DEVICE_CAPABILITY,
|
|
||||||
"libinput_device_capability",
|
|
||||||
)?;
|
|
||||||
write_ty(&mut f, libinput::LIBINPUT_KEY_STATE, "libinput_key_state")?;
|
|
||||||
write_ty(&mut f, libinput::LIBINPUT_LED, "libinput_led")?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_BUTTON_STATE,
|
|
||||||
"libinput_button_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_POINTER_AXIS,
|
|
||||||
"libinput_pointer_axis",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_POINTER_AXIS_SOURCE,
|
|
||||||
"libinput_pointer_axis_source",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_TABLET_PAD_RING_AXIS_SOURCE,
|
|
||||||
"libinput_tablet_pad_ring_axis_source",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_TABLET_PAD_STRIP_AXIS_SOURCE,
|
|
||||||
"libinput_tablet_pad_strip_axis_source",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_TABLET_TOOL_TYPE,
|
|
||||||
"libinput_tablet_tool_type",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_TABLET_TOOL_PROXIMITY_STATE,
|
|
||||||
"libinput_tablet_tool_proximity_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_TABLET_TOOL_TIP_STATE,
|
|
||||||
"libinput_tablet_tool_tip_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_SWITCH_STATE,
|
|
||||||
"libinput_switch_state",
|
|
||||||
)?;
|
|
||||||
write_ty(&mut f, libinput::LIBINPUT_SWITCH, "libinput_switch")?;
|
|
||||||
write_ty(&mut f, libinput::LIBINPUT_EVENT_TYPE, "libinput_event_type")?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_STATUS,
|
|
||||||
"libinput_config_status",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_ACCEL_PROFILE,
|
|
||||||
"libinput_config_accel_profile",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_TAP_STATE,
|
|
||||||
"libinput_config_tap_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_DRAG_STATE,
|
|
||||||
"libinput_config_drag_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_DRAG_LOCK_STATE,
|
|
||||||
"libinput_config_drag_lock_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_CLICK_METHOD,
|
|
||||||
"libinput_config_click_method",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
libinput::LIBINPUT_CONFIG_MIDDLE_EMULATION_STATE,
|
|
||||||
"libinput_config_middle_emulation_state",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut f = open("pango_tys.rs")?;
|
|
||||||
write_ty(&mut f, pango::CAIRO_FORMATS, "cairo_format_t")?;
|
|
||||||
write_ty(&mut f, pango::CAIRO_STATUSES, "cairo_status_t")?;
|
|
||||||
write_ty(&mut f, pango::CAIRO_OPERATORS, "cairo_operator_t")?;
|
|
||||||
write_ty(&mut f, pango::PANGO_ELLIPSIZE_MODES, "PangoEllipsizeMode_")?;
|
|
||||||
|
|
||||||
let mut f = open("fontconfig_tys.rs")?;
|
let mut f = open("fontconfig_tys.rs")?;
|
||||||
write_ty(&mut f, fontconfig::FC_MATCH_KINDS, "FcMatchKind")?;
|
write_ty(&mut f, fontconfig::FC_MATCH_KINDS, "FcMatchKind")?;
|
||||||
write_ty(&mut f, fontconfig::FC_RESULTS, "FcResult")?;
|
write_ty(&mut f, fontconfig::FC_RESULTS, "FcResult")?;
|
||||||
|
|
|
||||||
|
|
@ -274,47 +274,15 @@ fn write_message<W: Write>(f: &mut W, obj: &str, message: &Message) -> Result<()
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
enum RequestHandlerDirection {
|
|
||||||
Request,
|
|
||||||
Event,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_request_handler<W: Write>(
|
fn write_request_handler<W: Write>(
|
||||||
f: &mut W,
|
f: &mut W,
|
||||||
camel_obj_name: &str,
|
camel_obj_name: &str,
|
||||||
messages: &[Lined<Message>],
|
messages: &[Lined<Message>],
|
||||||
direction: RequestHandlerDirection,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let snake_direction;
|
|
||||||
let camel_direction;
|
|
||||||
let parent;
|
|
||||||
let parser;
|
|
||||||
let error;
|
|
||||||
let param;
|
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
match direction {
|
|
||||||
RequestHandlerDirection::Request => {
|
|
||||||
snake_direction = "request";
|
|
||||||
camel_direction = "Request";
|
|
||||||
parent = "crate::object::Object";
|
|
||||||
parser = "crate::client::Client";
|
|
||||||
error = "crate::client::ClientError";
|
|
||||||
param = "req";
|
|
||||||
}
|
|
||||||
RequestHandlerDirection::Event => {
|
|
||||||
snake_direction = "event";
|
|
||||||
camel_direction = "Event";
|
|
||||||
parent = "crate::wl_usr::usr_object::UsrObject";
|
|
||||||
parser = "crate::wl_usr::UsrCon";
|
|
||||||
error = "crate::wl_usr::UsrConError";
|
|
||||||
param = "ev";
|
|
||||||
writeln!(f, " #[allow(clippy::allow_attributes, dead_code)]")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
" pub trait {camel_obj_name}{camel_direction}Handler: {parent} + Sized {{"
|
" pub trait {camel_obj_name}RequestHandler: crate::object::Object + Sized {{"
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, " type Error: std::error::Error;")?;
|
writeln!(f, " type Error: std::error::Error;")?;
|
||||||
for message in messages {
|
for message in messages {
|
||||||
|
|
@ -326,24 +294,24 @@ fn write_request_handler<W: Write>(
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
" fn {}(&self, {param}: {}{lt}, _slf: &Rc<Self>) -> Result<(), Self::Error>;",
|
" fn {}(&self, req: {}{lt}, _slf: &Rc<Self>) -> Result<(), Self::Error>;",
|
||||||
msg.safe_name, msg.camel_name
|
msg.safe_name, msg.camel_name
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(f, " #[inline(always)]")?;
|
writeln!(f, " #[inline(always)]")?;
|
||||||
writeln!(f, " fn handle_{snake_direction}_impl(")?;
|
writeln!(f, " fn handle_request_impl(")?;
|
||||||
writeln!(f, " self: Rc<Self>,")?;
|
writeln!(f, " self: Rc<Self>,")?;
|
||||||
writeln!(f, " client: &{parser},")?;
|
writeln!(f, " client: &crate::client::Client,")?;
|
||||||
writeln!(f, " req: u32,")?;
|
writeln!(f, " req: u32,")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
" parser: crate::utils::buffd::MsgParser<'_, '_>,"
|
" parser: crate::utils::buffd::MsgParser<'_, '_>,"
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, " ) -> Result<(), {error}> {{")?;
|
writeln!(f, " ) -> Result<(), crate::client::ClientError> {{")?;
|
||||||
if messages.is_empty() {
|
if messages.is_empty() {
|
||||||
writeln!(f, " #![allow(unused_variables)]")?;
|
writeln!(f, " #![allow(unused_variables)]")?;
|
||||||
writeln!(f, " Err({error}::InvalidMethod)")?;
|
writeln!(f, " Err(crate::client::ClientError::InvalidMethod)")?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(f, " let method;")?;
|
writeln!(f, " let method;")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
@ -379,10 +347,10 @@ fn write_request_handler<W: Write>(
|
||||||
}
|
}
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
" _ => return Err({error}::InvalidMethod),"
|
" _ => return Err(crate::client::ClientError::InvalidMethod),"
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, " }};")?;
|
writeln!(f, " }};")?;
|
||||||
writeln!(f, " Err({error}::MethodError {{")?;
|
writeln!(f, " Err(crate::client::ClientError::MethodError {{")?;
|
||||||
writeln!(f, " interface: {camel_obj_name},")?;
|
writeln!(f, " interface: {camel_obj_name},")?;
|
||||||
writeln!(f, " id: self.id(),")?;
|
writeln!(f, " id: self.id(),")?;
|
||||||
writeln!(f, " method,")?;
|
writeln!(f, " method,")?;
|
||||||
|
|
@ -417,6 +385,7 @@ fn write_file<W: Write>(
|
||||||
let messages = parse_messages(&contents)?;
|
let messages = parse_messages(&contents)?;
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(f, "pub mod {} {{", obj_name)?;
|
writeln!(f, "pub mod {} {{", obj_name)?;
|
||||||
|
writeln!(f, " #![allow(dead_code)]")?;
|
||||||
writeln!(f, " use super::*;")?;
|
writeln!(f, " use super::*;")?;
|
||||||
for message in messages.requests.iter().chain(messages.events.iter()) {
|
for message in messages.requests.iter().chain(messages.events.iter()) {
|
||||||
write_message(f, &camel_obj_name, &message.val)?;
|
write_message(f, &camel_obj_name, &message.val)?;
|
||||||
|
|
@ -425,13 +394,6 @@ fn write_file<W: Write>(
|
||||||
f,
|
f,
|
||||||
&camel_obj_name,
|
&camel_obj_name,
|
||||||
&messages.requests,
|
&messages.requests,
|
||||||
RequestHandlerDirection::Request,
|
|
||||||
)?;
|
|
||||||
write_request_handler(
|
|
||||||
f,
|
|
||||||
&camel_obj_name,
|
|
||||||
&messages.events,
|
|
||||||
RequestHandlerDirection::Event,
|
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, "}}")?;
|
writeln!(f, "}}")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
12
crates/allocator/Cargo.toml
Normal file
12
crates/allocator/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-allocator"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-formats = { version = "0.1.0", path = "../formats" }
|
||||||
|
jay-video-types = { version = "0.1.0", path = "../video-types" }
|
||||||
|
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
95
crates/allocator/src/lib.rs
Normal file
95
crates/allocator/src/lib.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
use {
|
||||||
|
jay_formats::Format,
|
||||||
|
jay_video_types::{
|
||||||
|
Modifier,
|
||||||
|
dmabuf::{DmaBuf, DmaBufIds},
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
error::Error,
|
||||||
|
ops::{BitOr, BitOrAssign, Not},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{OwnedFd, c},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error(transparent)]
|
||||||
|
pub struct AllocatorError(#[from] pub Box<dyn Error + Send>);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct BufferUsage(u32);
|
||||||
|
|
||||||
|
impl BufferUsage {
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(self, other: Self) -> bool {
|
||||||
|
self.0 & other.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for BufferUsage {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 | rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign for BufferUsage {
|
||||||
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
|
self.0 |= rhs.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for BufferUsage {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
Self(!self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const BO_USE_SCANOUT: BufferUsage = BufferUsage(1 << 0);
|
||||||
|
pub const BO_USE_CURSOR: BufferUsage = BufferUsage(1 << 1);
|
||||||
|
pub const BO_USE_RENDERING: BufferUsage = BufferUsage(1 << 2);
|
||||||
|
pub const BO_USE_WRITE: BufferUsage = BufferUsage(1 << 3);
|
||||||
|
pub const BO_USE_LINEAR: BufferUsage = BufferUsage(1 << 4);
|
||||||
|
pub const BO_USE_PROTECTED: BufferUsage = BufferUsage(1 << 5);
|
||||||
|
|
||||||
|
pub trait Allocator {
|
||||||
|
fn drm(&self) -> Option<&dyn AllocatorDrm>;
|
||||||
|
fn create_bo(
|
||||||
|
&self,
|
||||||
|
dma_buf_ids: &DmaBufIds,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
format: &'static Format,
|
||||||
|
modifiers: &[Modifier],
|
||||||
|
usage: BufferUsage,
|
||||||
|
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
|
||||||
|
fn import_dmabuf(
|
||||||
|
&self,
|
||||||
|
dmabuf: &DmaBuf,
|
||||||
|
usage: BufferUsage,
|
||||||
|
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AllocatorDrm {
|
||||||
|
fn dev(&self) -> c::dev_t;
|
||||||
|
fn dup_render_fd(&self) -> Result<Rc<OwnedFd>, AllocatorError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BufferObject {
|
||||||
|
fn dmabuf(&self) -> &DmaBuf;
|
||||||
|
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
|
||||||
|
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MappedBuffer {
|
||||||
|
unsafe fn data(&self) -> &[u8];
|
||||||
|
fn data_ptr(&self) -> *mut u8;
|
||||||
|
fn stride(&self) -> i32;
|
||||||
|
}
|
||||||
14
crates/async-engine/Cargo.toml
Normal file
14
crates/async-engine/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-async-engine"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-time = { version = "0.1.0", path = "../time" }
|
||||||
|
jay-tracy = { version = "0.1.0", path = "../tracy" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
it = []
|
||||||
|
tracy = ["jay-tracy/tracy"]
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{AsyncEngine, Phase},
|
||||||
async_engine::{AsyncEngine, Phase},
|
jay_tracy::ZoneName,
|
||||||
tracy::ZoneName,
|
jay_utils::{
|
||||||
utils::{
|
numcell::NumCell,
|
||||||
numcell::NumCell,
|
ptr_ext::{MutPtrExt, PtrExt},
|
||||||
ptr_ext::{MutPtrExt, PtrExt},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, UnsafeCell},
|
cell::{Cell, UnsafeCell},
|
||||||
|
|
@ -142,7 +140,7 @@ impl AsyncEngine {
|
||||||
}),
|
}),
|
||||||
waker: Cell::new(None),
|
waker: Cell::new(None),
|
||||||
queue: self.clone(),
|
queue: self.clone(),
|
||||||
zone: create_zone_name!("task:{}", name),
|
zone: jay_tracy::create_zone_name!("task:{}", name),
|
||||||
});
|
});
|
||||||
unsafe {
|
unsafe {
|
||||||
f.schedule_run();
|
f.schedule_run();
|
||||||
|
|
@ -254,7 +252,7 @@ impl<T, F: Future<Output = T>> Task<T, F> {
|
||||||
|
|
||||||
let mut ctx = Context::from_waker(&waker);
|
let mut ctx = Context::from_waker(&waker);
|
||||||
let poll = {
|
let poll = {
|
||||||
dynamic_zone!(self.zone);
|
jay_tracy::dynamic_zone!(self.zone);
|
||||||
Pin::new_unchecked(&mut *data.future).poll(&mut ctx)
|
Pin::new_unchecked(&mut *data.future).poll(&mut ctx)
|
||||||
};
|
};
|
||||||
if let Poll::Ready(d) = poll {
|
if let Poll::Ready(d) = poll {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::async_engine::AsyncEngine,
|
crate::AsyncEngine,
|
||||||
std::{
|
std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
169
crates/async-engine/src/lib.rs
Normal file
169
crates/async-engine/src/lib.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
mod ae_task;
|
||||||
|
mod ae_yield;
|
||||||
|
mod run_toplevel;
|
||||||
|
|
||||||
|
pub use {ae_task::SpawnedFuture, ae_yield::Yield, run_toplevel::*};
|
||||||
|
use {
|
||||||
|
crate::ae_task::Runnable,
|
||||||
|
jay_time::Time,
|
||||||
|
jay_utils::{array, numcell::NumCell, syncqueue::SyncQueue},
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::VecDeque,
|
||||||
|
future::Future,
|
||||||
|
rc::Rc,
|
||||||
|
task::Waker,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum Phase {
|
||||||
|
EventHandling,
|
||||||
|
Layout,
|
||||||
|
PostLayout,
|
||||||
|
Present,
|
||||||
|
}
|
||||||
|
const NUM_PHASES: usize = 4;
|
||||||
|
|
||||||
|
pub struct AsyncEngine {
|
||||||
|
num_queued: NumCell<usize>,
|
||||||
|
queues: [SyncQueue<Runnable>; NUM_PHASES],
|
||||||
|
iteration: NumCell<u64>,
|
||||||
|
yields: SyncQueue<Waker>,
|
||||||
|
stash: RefCell<VecDeque<Runnable>>,
|
||||||
|
yield_stash: RefCell<VecDeque<Waker>>,
|
||||||
|
stopped: Cell<bool>,
|
||||||
|
now: Cell<Option<Time>>,
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
idle: Cell<Option<Waker>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncEngine {
|
||||||
|
pub fn new() -> Rc<Self> {
|
||||||
|
Rc::new(Self {
|
||||||
|
num_queued: Default::default(),
|
||||||
|
queues: array::from_fn(|_| Default::default()),
|
||||||
|
iteration: Default::default(),
|
||||||
|
yields: Default::default(),
|
||||||
|
stash: Default::default(),
|
||||||
|
yield_stash: Default::default(),
|
||||||
|
stopped: Cell::new(false),
|
||||||
|
now: Default::default(),
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
idle: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&self) {
|
||||||
|
self.stopped.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.stash.borrow_mut().clear();
|
||||||
|
self.yield_stash.borrow_mut().clear();
|
||||||
|
self.yields.take();
|
||||||
|
for queue in &self.queues {
|
||||||
|
queue.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn<T, F: Future<Output = T> + 'static>(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
name: &str,
|
||||||
|
f: F,
|
||||||
|
) -> SpawnedFuture<T> {
|
||||||
|
self.spawn_(name, Phase::EventHandling, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn2<T, F: Future<Output = T> + 'static>(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
name: &str,
|
||||||
|
phase: Phase,
|
||||||
|
f: F,
|
||||||
|
) -> SpawnedFuture<T> {
|
||||||
|
self.spawn_(name, phase, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yield_now(self: &Rc<Self>) -> Yield {
|
||||||
|
Yield {
|
||||||
|
iteration: self.iteration(),
|
||||||
|
queue: self.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch(&self) {
|
||||||
|
let mut stash = self.stash.borrow_mut();
|
||||||
|
let mut yield_stash = self.yield_stash.borrow_mut();
|
||||||
|
loop {
|
||||||
|
if self.num_queued.get() == 0 {
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
if let Some(idle) = self.idle.take() {
|
||||||
|
idle.wake();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.now.take();
|
||||||
|
let mut phase = 0;
|
||||||
|
while phase < NUM_PHASES {
|
||||||
|
self.queues[phase].swap(&mut *stash);
|
||||||
|
if stash.is_empty() {
|
||||||
|
phase += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.num_queued.fetch_sub(stash.len());
|
||||||
|
while let Some(runnable) = stash.pop_front() {
|
||||||
|
runnable.run();
|
||||||
|
if self.stopped.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.iteration.fetch_add(1);
|
||||||
|
self.yields.swap(&mut *yield_stash);
|
||||||
|
while let Some(waker) = yield_stash.pop_front() {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
pub async fn idle(&self) {
|
||||||
|
use std::{future::poll_fn, task::Poll};
|
||||||
|
let mut register = true;
|
||||||
|
poll_fn(|ctx| {
|
||||||
|
if register {
|
||||||
|
self.idle.set(Some(ctx.waker().clone()));
|
||||||
|
register = false;
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&self, runnable: Runnable, phase: Phase) {
|
||||||
|
self.queues[phase as usize].push(runnable);
|
||||||
|
self.num_queued.fetch_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_yield(&self, waker: Waker) {
|
||||||
|
self.yields.push(waker);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iteration(&self) -> u64 {
|
||||||
|
self.iteration.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now(&self) -> Time {
|
||||||
|
match self.now.get() {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
let now = Time::now_unchecked();
|
||||||
|
self.now.set(Some(now));
|
||||||
|
now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{AsyncEngine, SpawnedFuture},
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
jay_utils::queue::AsyncQueue,
|
||||||
utils::queue::AsyncQueue,
|
|
||||||
},
|
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
12
crates/bufio/Cargo.toml
Normal file
12
crates/bufio/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-bufio"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "../io-uring" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
jay_io_uring::{IoUring, IoUringError},
|
||||||
io_uring::{IoUring, IoUringError},
|
jay_utils::{
|
||||||
utils::{
|
buf::{Buf, DynamicBuf},
|
||||||
buf::{Buf, DynamicBuf},
|
queue::AsyncQueue,
|
||||||
queue::AsyncQueue,
|
stack::Stack,
|
||||||
stack::Stack,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
8
crates/bugs/Cargo.toml
Normal file
8
crates/bugs/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-bugs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ahash = "0.8.7"
|
||||||
38
crates/bugs/src/lib.rs
Normal file
38
crates/bugs/src/lib.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use {ahash::AHashMap, std::sync::LazyLock};
|
||||||
|
|
||||||
|
static BUGS: LazyLock<AHashMap<&'static str, Bugs>> = LazyLock::new(|| {
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
map.insert(
|
||||||
|
"chromium",
|
||||||
|
Bugs {
|
||||||
|
respect_min_max_size: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
"Alacritty",
|
||||||
|
Bugs {
|
||||||
|
min_width: Some(100),
|
||||||
|
min_height: Some(100),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn get(app_id: &str) -> &'static Bugs {
|
||||||
|
BUGS.get(app_id).unwrap_or(&NONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static NONE: Bugs = Bugs {
|
||||||
|
respect_min_max_size: false,
|
||||||
|
min_width: None,
|
||||||
|
min_height: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Bugs {
|
||||||
|
pub respect_min_max_size: bool,
|
||||||
|
pub min_width: Option<i32>,
|
||||||
|
pub min_height: Option<i32>,
|
||||||
|
}
|
||||||
18
crates/clientmem/Cargo.toml
Normal file
18
crates/clientmem/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-clientmem"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-cpu-worker = { version = "0.1.0", path = "../cpu-worker" }
|
||||||
|
jay-gfx-types = { version = "0.1.0", path = "../gfx-types" }
|
||||||
|
jay-tracy = { version = "0.1.0", path = "../tracy" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
log = "0.4.20"
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
tracy = ["jay-tracy/tracy"]
|
||||||
331
crates/clientmem/src/lib.rs
Normal file
331
crates/clientmem/src/lib.rs
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
use {
|
||||||
|
jay_cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
|
||||||
|
jay_gfx_types::{ShmMemory, ShmMemoryBacking},
|
||||||
|
jay_utils::{
|
||||||
|
oserror::{OsError, OsErrorExt2},
|
||||||
|
page_size::page_size,
|
||||||
|
vec_ext::VecExt,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::Cell,
|
||||||
|
error::Error,
|
||||||
|
mem::{ManuallyDrop, MaybeUninit},
|
||||||
|
ops::Deref,
|
||||||
|
ptr,
|
||||||
|
rc::Rc,
|
||||||
|
sync::atomic::{Ordering, compiler_fence},
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{
|
||||||
|
OwnedFd, Pod,
|
||||||
|
c::{self, raise},
|
||||||
|
ftruncate,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ClientMemClient<'a> {
|
||||||
|
pub comm: &'a str,
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ClientMemError {
|
||||||
|
#[error("Could not install the sigbus handler")]
|
||||||
|
SigactionFailed(#[source] jay_utils::oserror::OsError),
|
||||||
|
#[error("A SIGBUS occurred while accessing mapped memory")]
|
||||||
|
Sigbus,
|
||||||
|
#[error("mmap failed")]
|
||||||
|
MmapFailed(#[source] jay_utils::oserror::OsError),
|
||||||
|
#[error("Length was not a multiple of the data element size")]
|
||||||
|
InvalidLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientMem {
|
||||||
|
fd: ManuallyDrop<Rc<OwnedFd>>,
|
||||||
|
failed: Cell<bool>,
|
||||||
|
sigbus_impossible: bool,
|
||||||
|
data: *const [Cell<u8>],
|
||||||
|
cpu: Option<Rc<CpuWorker>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ClientMemOffset {
|
||||||
|
mem: Rc<ClientMem>,
|
||||||
|
offset: usize,
|
||||||
|
data: *const [Cell<u8>],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientMem {
|
||||||
|
pub fn new(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
len: usize,
|
||||||
|
read_only: bool,
|
||||||
|
client: Option<ClientMemClient<'_>>,
|
||||||
|
cpu: Option<&Rc<CpuWorker>>,
|
||||||
|
is_udmabuf: bool,
|
||||||
|
) -> Result<Self, ClientMemError> {
|
||||||
|
Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_private(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
len: usize,
|
||||||
|
read_only: bool,
|
||||||
|
client: Option<ClientMemClient<'_>>,
|
||||||
|
cpu: Option<&Rc<CpuWorker>>,
|
||||||
|
) -> Result<Self, ClientMemError> {
|
||||||
|
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new2(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
len: usize,
|
||||||
|
read_only: bool,
|
||||||
|
client: Option<ClientMemClient<'_>>,
|
||||||
|
cpu: Option<&Rc<CpuWorker>>,
|
||||||
|
flags: c::c_int,
|
||||||
|
is_udmabuf: bool,
|
||||||
|
) -> Result<Self, ClientMemError> {
|
||||||
|
let mut sigbus_impossible = is_udmabuf;
|
||||||
|
let mut real_size = None;
|
||||||
|
if !sigbus_impossible
|
||||||
|
&& let Ok(seals) = uapi::fcntl_get_seals(fd.raw())
|
||||||
|
&& seals & c::F_SEAL_SHRINK != 0
|
||||||
|
&& let Ok(stat) = uapi::fstat(fd.raw())
|
||||||
|
{
|
||||||
|
real_size = Some(stat.st_size as usize);
|
||||||
|
sigbus_impossible = stat.st_size as u64 >= len as u64;
|
||||||
|
}
|
||||||
|
if !sigbus_impossible && let Some(client) = client {
|
||||||
|
log::debug!(
|
||||||
|
"Client {} ({}) has created a shm buffer that might cause SIGBUS",
|
||||||
|
client.comm,
|
||||||
|
client.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let len = len.next_multiple_of(page_size());
|
||||||
|
if let Some(real_size) = real_size
|
||||||
|
&& real_size < len
|
||||||
|
{
|
||||||
|
let _ = ftruncate(fd.raw(), len as _);
|
||||||
|
}
|
||||||
|
let data = if len == 0 {
|
||||||
|
&mut [][..]
|
||||||
|
} else {
|
||||||
|
let prot = match read_only {
|
||||||
|
true => c::PROT_READ,
|
||||||
|
false => c::PROT_READ | c::PROT_WRITE,
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0);
|
||||||
|
if data == c::MAP_FAILED {
|
||||||
|
return Err(ClientMemError::MmapFailed(OsError::default()));
|
||||||
|
}
|
||||||
|
std::slice::from_raw_parts_mut(data as *mut Cell<u8>, len)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
fd: ManuallyDrop::new(fd.clone()),
|
||||||
|
failed: Cell::new(false),
|
||||||
|
sigbus_impossible,
|
||||||
|
data,
|
||||||
|
cpu: cpu.cloned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(self: &Rc<Self>, offset: usize, len: usize) -> ClientMemOffset {
|
||||||
|
let mem = unsafe { &*self.data };
|
||||||
|
ClientMemOffset {
|
||||||
|
mem: self.clone(),
|
||||||
|
offset,
|
||||||
|
data: &mem[offset..][..len],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fd(&self) -> &Rc<OwnedFd> {
|
||||||
|
&self.fd
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_sealed_memfd(&self) -> bool {
|
||||||
|
self.sigbus_impossible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientMemOffset {
|
||||||
|
pub fn pool(&self) -> &ClientMem {
|
||||||
|
&self.mem
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> usize {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptr(&self) -> *const [Cell<u8>] {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn access<T, F: FnOnce(&[Cell<u8>]) -> T>(&self, f: F) -> Result<T, ClientMemError> {
|
||||||
|
unsafe {
|
||||||
|
if self.mem.sigbus_impossible {
|
||||||
|
return Ok(f(&*self.data));
|
||||||
|
}
|
||||||
|
let mref = MemRef {
|
||||||
|
mem: &*self.mem,
|
||||||
|
outer: MEM.get(),
|
||||||
|
};
|
||||||
|
MEM.set(&mref);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
let res = f(&*self.data);
|
||||||
|
MEM.set(mref.outer);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
match self.mem.failed.get() {
|
||||||
|
true => Err(ClientMemError::Sigbus),
|
||||||
|
_ => Ok(res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<T: Pod>(&self, dst: &mut Vec<T>) -> Result<(), ClientMemError> {
|
||||||
|
if self.data.len().checked_rem(std::mem::size_of::<T>()) != Some(0) {
|
||||||
|
return Err(ClientMemError::InvalidLength);
|
||||||
|
}
|
||||||
|
self.access(|v| {
|
||||||
|
let len_elements = v.len() / std::mem::size_of::<T>();
|
||||||
|
dst.reserve(len_elements);
|
||||||
|
let (_, unused) = dst.split_at_spare_mut_bytes_ext();
|
||||||
|
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
|
||||||
|
unsafe {
|
||||||
|
dst.set_len(dst.len() + len_elements);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ClientMem {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
|
||||||
|
if let Some(cpu) = &self.cpu {
|
||||||
|
let pending = cpu.submit(Box::new(CloseMemWork {
|
||||||
|
fd: Rc::try_unwrap(fd).ok(),
|
||||||
|
data: self.data,
|
||||||
|
}));
|
||||||
|
pending.detach();
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
c::munmap(self.data as _, self.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MemRef {
|
||||||
|
mem: *const ClientMem,
|
||||||
|
outer: *const MemRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn kill() -> ! {
|
||||||
|
unsafe {
|
||||||
|
c::signal(c::SIGBUS, c::SIG_DFL);
|
||||||
|
raise(c::SIGBUS);
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) {
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(sig, c::SIGBUS);
|
||||||
|
let mut memr_ptr = MEM.get();
|
||||||
|
while !memr_ptr.is_null() {
|
||||||
|
let memr = &*memr_ptr;
|
||||||
|
let mem = &*memr.mem;
|
||||||
|
let lo = mem.data as *mut u8 as usize;
|
||||||
|
let hi = lo + mem.len();
|
||||||
|
let fault_addr = info.si_addr() as usize;
|
||||||
|
if fault_addr < lo || fault_addr >= hi {
|
||||||
|
memr_ptr = memr.outer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let res = c::mmap64(
|
||||||
|
lo as _,
|
||||||
|
hi - lo,
|
||||||
|
c::PROT_WRITE | c::PROT_READ,
|
||||||
|
c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
if res == c::MAP_FAILED {
|
||||||
|
kill();
|
||||||
|
}
|
||||||
|
mem.failed.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() -> Result<(), ClientMemError> {
|
||||||
|
unsafe {
|
||||||
|
let mut action: c::sigaction = MaybeUninit::zeroed().assume_init();
|
||||||
|
action.sa_sigaction =
|
||||||
|
sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _;
|
||||||
|
action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO;
|
||||||
|
let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut());
|
||||||
|
uapi::map_err!(res)
|
||||||
|
.map(drop)
|
||||||
|
.map_os_err(ClientMemError::SigactionFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CloseMemWork {
|
||||||
|
fd: Option<OwnedFd>,
|
||||||
|
data: *const [Cell<u8>],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for CloseMemWork {}
|
||||||
|
|
||||||
|
impl CpuJob for CloseMemWork {
|
||||||
|
fn work(&mut self) -> &mut dyn CpuWork {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn completed(self: Box<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuWork for CloseMemWork {
|
||||||
|
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||||
|
jay_tracy::zone!("CloseMemWork");
|
||||||
|
self.fd.take();
|
||||||
|
unsafe {
|
||||||
|
c::munmap(self.data as _, self.data.len());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShmMemory for ClientMemOffset {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn safe_access(&self) -> ShmMemoryBacking {
|
||||||
|
match self.mem.is_sealed_memfd() {
|
||||||
|
true => ShmMemoryBacking::Ptr(self.data),
|
||||||
|
false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
|
||||||
|
self.access(f).map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
8
crates/cmm/Cargo.toml
Normal file
8
crates/cmm/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-cmm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cmm::{
|
cmm_eotf::Eotf,
|
||||||
cmm_eotf::Eotf,
|
cmm_luminance::{Luminance, TargetLuminance, white_balance},
|
||||||
cmm_luminance::{Luminance, TargetLuminance, white_balance},
|
cmm_manager::Shared,
|
||||||
cmm_manager::Shared,
|
cmm_primaries::{NamedPrimaries, Primaries},
|
||||||
cmm_primaries::{NamedPrimaries, Primaries},
|
cmm_render_intent::RenderIntent,
|
||||||
cmm_render_intent::RenderIntent,
|
cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment},
|
||||||
cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment},
|
|
||||||
},
|
|
||||||
utils::ordered_float::F64,
|
|
||||||
},
|
},
|
||||||
|
jay_utils::ordered_float::F64,
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::utils::ordered_float::F32;
|
use jay_utils::ordered_float::F32;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Eotf {
|
pub enum Eotf {
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
cmm::{
|
cmm_render_intent::RenderIntent,
|
||||||
cmm_render_intent::RenderIntent,
|
cmm_transform::{ColorMatrix, Xyz},
|
||||||
cmm_transform::{ColorMatrix, Xyz},
|
|
||||||
},
|
|
||||||
utils::ordered_float::F64,
|
|
||||||
};
|
};
|
||||||
|
use jay_utils::ordered_float::F64;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct Luminance {
|
pub struct Luminance {
|
||||||
|
|
@ -38,7 +36,6 @@ impl Luminance {
|
||||||
white: F64(203.0),
|
white: F64(203.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub const HLG: Self = Self {
|
pub const HLG: Self = Self {
|
||||||
min: F64(0.005),
|
min: F64(0.005),
|
||||||
max: F64(1000.0),
|
max: F64(1000.0),
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cmm::{
|
cmm_description::{
|
||||||
cmm_description::{
|
ColorDescription, ColorDescriptionIds, LinearColorDescription,
|
||||||
ColorDescription, ColorDescriptionIds, LinearColorDescription,
|
LinearColorDescriptionId, LinearColorDescriptionIds,
|
||||||
LinearColorDescriptionId, LinearColorDescriptionIds,
|
|
||||||
},
|
|
||||||
cmm_eotf::Eotf,
|
|
||||||
cmm_luminance::{Luminance, TargetLuminance},
|
|
||||||
cmm_primaries::{NamedPrimaries, Primaries},
|
|
||||||
},
|
},
|
||||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell, ordered_float::F64},
|
cmm_eotf::Eotf,
|
||||||
|
cmm_luminance::{Luminance, TargetLuminance},
|
||||||
|
cmm_primaries::{NamedPrimaries, Primaries},
|
||||||
},
|
},
|
||||||
|
jay_utils::{copyhashmap::CopyHashMap, numcell::NumCell, ordered_float::F64},
|
||||||
std::rc::{Rc, Weak},
|
std::rc::{Rc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use {crate::utils::ordered_float::F64, std::hash::Hash};
|
use {jay_utils::ordered_float::F64, std::hash::Hash};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub enum NamedPrimaries {
|
pub enum NamedPrimaries {
|
||||||
|
|
@ -1,11 +1,3 @@
|
||||||
use crate::{
|
|
||||||
ifs::color_management::{
|
|
||||||
ABSOLUTE_NO_ADAPTATION_SINCE, RENDER_INTENT_ABSOLUTE_NO_ADAPTATION,
|
|
||||||
RENDER_INTENT_PERCEPTUAL, RENDER_INTENT_RELATIVE, RENDER_INTENT_RELATIVE_BPC,
|
|
||||||
},
|
|
||||||
object::Version,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||||
pub enum RenderIntent {
|
pub enum RenderIntent {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -16,19 +8,6 @@ pub enum RenderIntent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderIntent {
|
impl RenderIntent {
|
||||||
pub fn from_wayland(intent: u32, version: Version) -> Option<Self> {
|
|
||||||
let res = match intent {
|
|
||||||
RENDER_INTENT_PERCEPTUAL => Self::Perceptual,
|
|
||||||
RENDER_INTENT_RELATIVE => Self::Relative,
|
|
||||||
RENDER_INTENT_RELATIVE_BPC => Self::RelativeBpc,
|
|
||||||
RENDER_INTENT_ABSOLUTE_NO_ADAPTATION if version >= ABSOLUTE_NO_ADAPTATION_SINCE => {
|
|
||||||
Self::AbsoluteNoAdaptation
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn black_point_compensation(self) -> bool {
|
pub fn black_point_compensation(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
RenderIntent::Perceptual => true,
|
RenderIntent::Perceptual => true,
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mod matrices {
|
mod matrices {
|
||||||
use crate::{cmm::cmm_primaries::Primaries, utils::ordered_float::F64};
|
use {crate::cmm_primaries::Primaries, jay_utils::ordered_float::F64};
|
||||||
|
|
||||||
fn check(primaries: Primaries, expected: [[f64; 4]; 3]) {
|
fn check(primaries: Primaries, expected: [[f64; 4]; 3]) {
|
||||||
let (ltg, gtl) = primaries.matrices();
|
let (ltg, gtl) = primaries.matrices();
|
||||||
|
|
@ -134,7 +134,7 @@ mod matrices {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod transforms {
|
mod transforms {
|
||||||
use crate::cmm::{
|
use crate::{
|
||||||
cmm_eotf::Eotf, cmm_luminance::Luminance, cmm_manager::ColorManager,
|
cmm_eotf::Eotf, cmm_luminance::Luminance, cmm_manager::ColorManager,
|
||||||
cmm_primaries::Primaries, cmm_render_intent::RenderIntent,
|
cmm_primaries::Primaries, cmm_render_intent::RenderIntent,
|
||||||
};
|
};
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::cmm_primaries::Primaries,
|
||||||
cmm::{cmm_eotf::Eotf, cmm_primaries::Primaries},
|
jay_utils::ordered_float::F64,
|
||||||
gfx_api::AlphaMode,
|
|
||||||
theme::Color,
|
|
||||||
utils::ordered_float::F64,
|
|
||||||
},
|
|
||||||
std::{
|
std::{
|
||||||
fmt,
|
fmt,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
|
@ -129,29 +125,6 @@ impl<T, U> Mul<[f64; 3]> for ColorMatrix<T, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Mul<Color> for ColorMatrix<T, U> {
|
|
||||||
type Output = Color;
|
|
||||||
|
|
||||||
fn mul(self, rhs: Color) -> Self::Output {
|
|
||||||
let mut rgba = rhs.to_array(Eotf::Linear);
|
|
||||||
let a = rgba[3];
|
|
||||||
if a < 1.0 && a > 0.0 {
|
|
||||||
for c in &mut rgba[..3] {
|
|
||||||
*c /= a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let [r, g, b] = self * [rgba[0] as f64, rgba[1] as f64, rgba[2] as f64];
|
|
||||||
Color::new(
|
|
||||||
Eotf::Linear,
|
|
||||||
AlphaMode::Straight,
|
|
||||||
r as f32,
|
|
||||||
g as f32,
|
|
||||||
b as f32,
|
|
||||||
a,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> ColorMatrix<T, U> {
|
impl<T, U> ColorMatrix<T, U> {
|
||||||
pub const fn new(m: [[f64; 4]; 3]) -> Self {
|
pub const fn new(m: [[f64; 4]; 3]) -> Self {
|
||||||
let m = [
|
let m = [
|
||||||
53
crates/cmm/src/lib.rs
Normal file
53
crates/cmm/src/lib.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
macro_rules! linear_ids {
|
||||||
|
($ids:ident, $id:ident, $ty:ty $(,)?) => {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct $ids {
|
||||||
|
next: jay_utils::numcell::NumCell<$ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for $ids {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next: jay_utils::numcell::NumCell::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $ids {
|
||||||
|
pub fn next(&self) -> $id {
|
||||||
|
$id(self.next.fetch_add(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct $id($ty);
|
||||||
|
|
||||||
|
impl $id {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn raw(&self) -> $ty {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn from_raw(id: $ty) -> Self {
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for $id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod cmm_description;
|
||||||
|
pub mod cmm_eotf;
|
||||||
|
pub mod cmm_luminance;
|
||||||
|
pub mod cmm_manager;
|
||||||
|
pub mod cmm_primaries;
|
||||||
|
pub mod cmm_render_intent;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod cmm_tests;
|
||||||
|
pub mod cmm_transform;
|
||||||
24
crates/cpu-worker/Cargo.toml
Normal file
24
crates/cpu-worker/Cargo.toml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-cpu-worker"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-async-engine = { version = "0.1.0", path = "../async-engine" }
|
||||||
|
jay-geometry = { version = "0.1.0", path = "../geometry" }
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "../io-uring" }
|
||||||
|
jay-tracy = { version = "0.1.0", path = "../tracy" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
log = { version = "0.4.20", features = ["std"] }
|
||||||
|
parking_lot = "0.12.1"
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
jay-wheel = { version = "0.1.0", path = "../wheel" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
it = []
|
||||||
|
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy"]
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{AsyncCpuWork, CpuWork},
|
||||||
cpu_worker::{AsyncCpuWork, CpuWork},
|
jay_geometry::Rect,
|
||||||
rect::Rect,
|
|
||||||
},
|
|
||||||
std::ptr,
|
std::ptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -34,7 +32,7 @@ impl ImgCopyWork {
|
||||||
|
|
||||||
impl CpuWork for ImgCopyWork {
|
impl CpuWork for ImgCopyWork {
|
||||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||||
zone!("ImgCopyWork");
|
jay_tracy::zone!("ImgCopyWork");
|
||||||
for rect in &self.rects {
|
for rect in &self.rects {
|
||||||
let mut offset = rect.y1() * self.stride + rect.x1() * self.bpp;
|
let mut offset = rect.y1() * self.stride + rect.x1() * self.bpp;
|
||||||
if rect.width() == self.width {
|
if rect.width() == self.width {
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{AsyncCpuWork, CompletedWork, CpuWork, WorkCompletion},
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
jay_async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
cpu_worker::{AsyncCpuWork, CompletedWork, CpuWork, WorkCompletion},
|
jay_io_uring::{IoUring, IoUringError, IoUringTaskId},
|
||||||
io_uring::{IoUring, IoUringError, IoUringTaskId},
|
|
||||||
},
|
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
ptr,
|
ptr,
|
||||||
509
crates/cpu-worker/src/lib.rs
Normal file
509
crates/cpu-worker/src/lib.rs
Normal file
|
|
@ -0,0 +1,509 @@
|
||||||
|
pub mod jobs;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use {
|
||||||
|
jay_async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
|
jay_io_uring::IoUring,
|
||||||
|
jay_utils::{
|
||||||
|
buf::TypedBuf,
|
||||||
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
numcell::NumCell,
|
||||||
|
oserror::{OsError, OsErrorExt2},
|
||||||
|
pipe::{Pipe, pipe},
|
||||||
|
ptr_ext::MutPtrExt,
|
||||||
|
queue::AsyncQueue,
|
||||||
|
stack::Stack,
|
||||||
|
},
|
||||||
|
parking_lot::{Condvar, Mutex},
|
||||||
|
std::{
|
||||||
|
any::Any,
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::VecDeque,
|
||||||
|
mem,
|
||||||
|
ptr::NonNull,
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
thread,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{OwnedFd, c},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait CpuJob {
|
||||||
|
fn work(&mut self) -> &mut dyn CpuWork;
|
||||||
|
fn completed(self: Box<Self>);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CpuWork: Send {
|
||||||
|
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>>;
|
||||||
|
|
||||||
|
fn cancel_async(&mut self, ring: &Rc<IoUring>) {
|
||||||
|
let _ = ring;
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||||
|
let _ = work;
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AsyncCpuWork: Any {
|
||||||
|
fn run(
|
||||||
|
self: Box<Self>,
|
||||||
|
eng: &Rc<AsyncEngine>,
|
||||||
|
ring: &Rc<IoUring>,
|
||||||
|
completion: WorkCompletion,
|
||||||
|
) -> SpawnedFuture<CompletedWork>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WorkCompletion {
|
||||||
|
worker: Rc<Worker>,
|
||||||
|
id: CpuJobId,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CompletedWork(());
|
||||||
|
|
||||||
|
impl WorkCompletion {
|
||||||
|
pub fn complete(self, work: Box<dyn AsyncCpuWork>) -> CompletedWork {
|
||||||
|
let job = self.worker.async_jobs.remove(&self.id).unwrap();
|
||||||
|
unsafe {
|
||||||
|
job.work.deref_mut().async_work_done(work);
|
||||||
|
}
|
||||||
|
self.worker.send_completion(self.id);
|
||||||
|
CompletedWork(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CpuWorker {
|
||||||
|
data: Rc<CpuWorkerData>,
|
||||||
|
_completions_listener: SpawnedFuture<()>,
|
||||||
|
_job_enqueuer: SpawnedFuture<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub struct PendingJob {
|
||||||
|
id: CpuJobId,
|
||||||
|
thread_data: Rc<CpuWorkerData>,
|
||||||
|
job_data: Rc<PendingJobData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||||
|
enum PendingJobState {
|
||||||
|
#[default]
|
||||||
|
Waiting,
|
||||||
|
Abandoned,
|
||||||
|
Completed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct PendingJobData {
|
||||||
|
job: Cell<Option<NonNull<dyn CpuJob>>>,
|
||||||
|
state: Cell<PendingJobState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Job {
|
||||||
|
New {
|
||||||
|
id: CpuJobId,
|
||||||
|
work: *mut dyn CpuWork,
|
||||||
|
},
|
||||||
|
Cancel {
|
||||||
|
id: CpuJobId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Job {}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CompletedJobsExchange {
|
||||||
|
queue: VecDeque<CpuJobId>,
|
||||||
|
condvar: Option<Arc<Condvar>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CpuWorkerData {
|
||||||
|
next: CpuJobIds,
|
||||||
|
jobs_to_enqueue: AsyncQueue<Job>,
|
||||||
|
new_jobs: Arc<Mutex<VecDeque<Job>>>,
|
||||||
|
have_new_jobs: Rc<OwnedFd>,
|
||||||
|
completed_jobs_remote: Arc<Mutex<CompletedJobsExchange>>,
|
||||||
|
completed_jobs_local: RefCell<VecDeque<CpuJobId>>,
|
||||||
|
have_completed_jobs: Rc<OwnedFd>,
|
||||||
|
pending_jobs: CopyHashMap<CpuJobId, Rc<PendingJobData>>,
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
_stop: OwnedFd,
|
||||||
|
pending_job_data_cache: Stack<Rc<PendingJobData>>,
|
||||||
|
sync_wake_condvar: Arc<Condvar>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CpuJobIds {
|
||||||
|
next: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CpuJobIds {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next: NumCell::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuJobIds {
|
||||||
|
fn next(&self) -> CpuJobId {
|
||||||
|
CpuJobId(self.next.fetch_add(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
|
struct CpuJobId(u64);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum CpuWorkerError {
|
||||||
|
#[error("Could not create a pipe")]
|
||||||
|
Pipe(#[source] OsError),
|
||||||
|
#[error("Could not create an eventfd")]
|
||||||
|
EventFd(#[source] OsError),
|
||||||
|
#[error("Could not dup an eventfd")]
|
||||||
|
Dup(#[source] OsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingJob {
|
||||||
|
pub fn detach(self) {
|
||||||
|
match self.job_data.state.get() {
|
||||||
|
PendingJobState::Waiting => {
|
||||||
|
self.job_data.state.set(PendingJobState::Abandoned);
|
||||||
|
}
|
||||||
|
PendingJobState::Abandoned => {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
PendingJobState::Completed => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CpuWorker {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.data.do_equeue_jobs();
|
||||||
|
if self.data.pending_jobs.is_not_empty() {
|
||||||
|
log::warn!("CpuWorker dropped with pending jobs. Completed jobs will not be triggered.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PendingJob {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match self.job_data.state.get() {
|
||||||
|
PendingJobState::Waiting => {
|
||||||
|
log::warn!("PendingJob dropped before completion. Blocking.");
|
||||||
|
let data = &self.thread_data;
|
||||||
|
let id = self.id;
|
||||||
|
self.job_data.state.set(PendingJobState::Abandoned);
|
||||||
|
data.jobs_to_enqueue.push(Job::Cancel { id });
|
||||||
|
data.do_equeue_jobs();
|
||||||
|
loop {
|
||||||
|
data.dispatch_completions();
|
||||||
|
if !data.pending_jobs.contains(&id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut remote = data.completed_jobs_remote.lock();
|
||||||
|
while remote.queue.is_empty() {
|
||||||
|
remote.condvar = Some(data.sync_wake_condvar.clone());
|
||||||
|
data.sync_wake_condvar.wait(&mut remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PendingJobState::Abandoned => {}
|
||||||
|
PendingJobState::Completed => {
|
||||||
|
self.thread_data
|
||||||
|
.pending_job_data_cache
|
||||||
|
.push(self.job_data.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuWorkerData {
|
||||||
|
fn clear(&self) {
|
||||||
|
self.jobs_to_enqueue.clear();
|
||||||
|
self.new_jobs.lock().clear();
|
||||||
|
self.completed_jobs_remote.lock().queue.clear();
|
||||||
|
self.completed_jobs_local.borrow_mut().clear();
|
||||||
|
self.pending_jobs.clear();
|
||||||
|
self.pending_job_data_cache.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_completions(self: Rc<Self>) {
|
||||||
|
let mut buf = TypedBuf::<u64>::new();
|
||||||
|
loop {
|
||||||
|
if let Err(e) = self.ring.read(&self.have_completed_jobs, buf.buf()).await {
|
||||||
|
log::error!("Could not wait for job completions: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.dispatch_completions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_completions(&self) {
|
||||||
|
let completions = &mut *self.completed_jobs_local.borrow_mut();
|
||||||
|
mem::swap(completions, &mut self.completed_jobs_remote.lock().queue);
|
||||||
|
while let Some(id) = completions.pop_front() {
|
||||||
|
let job_data = self.pending_jobs.remove(&id).unwrap();
|
||||||
|
let job = job_data.job.take().unwrap();
|
||||||
|
let job = unsafe { Box::from_raw(job.as_ptr()) };
|
||||||
|
match job_data.state.get() {
|
||||||
|
PendingJobState::Waiting => {
|
||||||
|
job_data.state.set(PendingJobState::Completed);
|
||||||
|
job.completed();
|
||||||
|
}
|
||||||
|
PendingJobState::Abandoned => {
|
||||||
|
self.pending_job_data_cache.push(job_data);
|
||||||
|
}
|
||||||
|
PendingJobState::Completed => {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn equeue_jobs(self: Rc<Self>) {
|
||||||
|
loop {
|
||||||
|
self.jobs_to_enqueue.non_empty().await;
|
||||||
|
self.do_equeue_jobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_equeue_jobs(&self) {
|
||||||
|
self.jobs_to_enqueue.move_to(&mut self.new_jobs.lock());
|
||||||
|
if let Err(e) = uapi::eventfd_write(self.have_new_jobs.raw(), 1) {
|
||||||
|
panic!("Could not signal eventfd: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuWorker {
|
||||||
|
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Result<Self, CpuWorkerError> {
|
||||||
|
let new_jobs: Arc<Mutex<VecDeque<Job>>> = Default::default();
|
||||||
|
let completed_jobs: Arc<Mutex<CompletedJobsExchange>> = Default::default();
|
||||||
|
let Pipe {
|
||||||
|
read: stop_read,
|
||||||
|
write: stop_write,
|
||||||
|
} = pipe().map_err(CpuWorkerError::Pipe)?;
|
||||||
|
let have_new_jobs = uapi::eventfd(0, c::EFD_CLOEXEC).map_os_err(CpuWorkerError::EventFd)?;
|
||||||
|
let have_completed_jobs =
|
||||||
|
uapi::eventfd(0, c::EFD_CLOEXEC).map_os_err(CpuWorkerError::EventFd)?;
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("cpu worker".to_string())
|
||||||
|
.spawn({
|
||||||
|
let new_jobs = new_jobs.clone();
|
||||||
|
let completed_jobs = completed_jobs.clone();
|
||||||
|
let have_new_jobs = uapi::fcntl_dupfd_cloexec(have_new_jobs.raw(), 0)
|
||||||
|
.map_os_err(CpuWorkerError::Dup)?;
|
||||||
|
let have_completed_jobs = uapi::fcntl_dupfd_cloexec(have_completed_jobs.raw(), 0)
|
||||||
|
.map_os_err(CpuWorkerError::Dup)?;
|
||||||
|
move || {
|
||||||
|
work(
|
||||||
|
new_jobs,
|
||||||
|
completed_jobs,
|
||||||
|
stop_write,
|
||||||
|
have_new_jobs,
|
||||||
|
have_completed_jobs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let data = Rc::new(CpuWorkerData {
|
||||||
|
next: Default::default(),
|
||||||
|
jobs_to_enqueue: Default::default(),
|
||||||
|
new_jobs,
|
||||||
|
have_new_jobs: Rc::new(have_new_jobs),
|
||||||
|
completed_jobs_remote: completed_jobs,
|
||||||
|
completed_jobs_local: Default::default(),
|
||||||
|
have_completed_jobs: Rc::new(have_completed_jobs),
|
||||||
|
pending_jobs: Default::default(),
|
||||||
|
ring: ring.clone(),
|
||||||
|
_stop: stop_read,
|
||||||
|
pending_job_data_cache: Default::default(),
|
||||||
|
sync_wake_condvar: Arc::new(Condvar::new()),
|
||||||
|
});
|
||||||
|
Ok(Self {
|
||||||
|
_completions_listener: eng.spawn(
|
||||||
|
"cpu worker completions",
|
||||||
|
data.clone().wait_for_completions(),
|
||||||
|
),
|
||||||
|
_job_enqueuer: eng.spawn("cpu worker enqueue", data.clone().equeue_jobs()),
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submit(&self, job: Box<dyn CpuJob>) -> PendingJob {
|
||||||
|
let mut job = NonNull::from(Box::leak(job));
|
||||||
|
let id = self.data.next.next();
|
||||||
|
self.data.jobs_to_enqueue.push(Job::New {
|
||||||
|
id,
|
||||||
|
work: unsafe { job.as_mut().work() },
|
||||||
|
});
|
||||||
|
let job_data = self.data.pending_job_data_cache.pop().unwrap_or_default();
|
||||||
|
job_data.job.set(Some(job));
|
||||||
|
job_data.state.set(PendingJobState::Waiting);
|
||||||
|
self.data.pending_jobs.set(id, job_data.clone());
|
||||||
|
PendingJob {
|
||||||
|
id,
|
||||||
|
thread_data: self.data.clone(),
|
||||||
|
job_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "it")]
|
||||||
|
pub fn wait_idle(&self) -> bool {
|
||||||
|
let was_idle = self.data.pending_jobs.is_empty();
|
||||||
|
loop {
|
||||||
|
self.data.dispatch_completions();
|
||||||
|
if self.data.pending_jobs.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut remote = self.data.completed_jobs_remote.lock();
|
||||||
|
while remote.queue.is_empty() {
|
||||||
|
remote.condvar = Some(self.data.sync_wake_condvar.clone());
|
||||||
|
self.data.sync_wake_condvar.wait(&mut remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
was_idle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn work(
|
||||||
|
new_jobs: Arc<Mutex<VecDeque<Job>>>,
|
||||||
|
completed_jobs: Arc<Mutex<CompletedJobsExchange>>,
|
||||||
|
stop: OwnedFd,
|
||||||
|
have_new_jobs: OwnedFd,
|
||||||
|
have_completed_jobs: OwnedFd,
|
||||||
|
) {
|
||||||
|
let eng = AsyncEngine::new();
|
||||||
|
let ring = IoUring::new(&eng, 32).unwrap();
|
||||||
|
let worker = Rc::new(Worker {
|
||||||
|
eng,
|
||||||
|
ring,
|
||||||
|
completed_jobs,
|
||||||
|
have_completed_jobs,
|
||||||
|
async_jobs: Default::default(),
|
||||||
|
stopped: Cell::new(false),
|
||||||
|
});
|
||||||
|
let _stop_listener = worker
|
||||||
|
.eng
|
||||||
|
.spawn("stop listener", worker.clone().handle_stop(stop));
|
||||||
|
let _new_job_listener = worker.eng.spawn(
|
||||||
|
"new job listener",
|
||||||
|
worker.clone().handle_new_jobs(new_jobs, have_new_jobs),
|
||||||
|
);
|
||||||
|
if let Err(e) = worker.ring.run() {
|
||||||
|
panic!("io_uring failed: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Worker {
|
||||||
|
eng: Rc<AsyncEngine>,
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
completed_jobs: Arc<Mutex<CompletedJobsExchange>>,
|
||||||
|
have_completed_jobs: OwnedFd,
|
||||||
|
async_jobs: CopyHashMap<CpuJobId, AsyncJob>,
|
||||||
|
stopped: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AsyncJob {
|
||||||
|
_future: SpawnedFuture<CompletedWork>,
|
||||||
|
work: *mut dyn CpuWork,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Worker {
|
||||||
|
async fn handle_stop(self: Rc<Self>, stop: OwnedFd) {
|
||||||
|
let stop = Rc::new(stop);
|
||||||
|
if let Err(e) = self.ring.poll(&stop, 0).await {
|
||||||
|
log::error!(
|
||||||
|
"Could not wait for stop fd to become readable: {}",
|
||||||
|
ErrorFmt(e)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert!(self.async_jobs.is_empty());
|
||||||
|
self.stopped.set(true);
|
||||||
|
self.ring.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_new_jobs(
|
||||||
|
self: Rc<Self>,
|
||||||
|
jobs_remote: Arc<Mutex<VecDeque<Job>>>,
|
||||||
|
new_jobs: OwnedFd,
|
||||||
|
) {
|
||||||
|
let mut buf = TypedBuf::<u64>::new();
|
||||||
|
let new_jobs = Rc::new(new_jobs);
|
||||||
|
let mut jobs = VecDeque::new();
|
||||||
|
loop {
|
||||||
|
if let Err(e) = self.ring.read(&new_jobs, buf.buf()).await {
|
||||||
|
if self.stopped.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
panic!(
|
||||||
|
"Could not wait for new jobs fd to be signaled: {}",
|
||||||
|
ErrorFmt(e),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mem::swap(&mut jobs, &mut *jobs_remote.lock());
|
||||||
|
while let Some(job) = jobs.pop_front() {
|
||||||
|
self.handle_new_job(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_new_job(self: &Rc<Self>, job: Job) {
|
||||||
|
match job {
|
||||||
|
Job::Cancel { id } => {
|
||||||
|
let mut jobs = self.async_jobs.lock();
|
||||||
|
if let Some(job) = jobs.get_mut(&id) {
|
||||||
|
unsafe {
|
||||||
|
job.work.deref_mut().cancel_async(&self.ring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Job::New { id, work } => match unsafe { work.deref_mut() }.run() {
|
||||||
|
None => {
|
||||||
|
self.send_completion(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(w) => {
|
||||||
|
let completion = WorkCompletion {
|
||||||
|
worker: self.clone(),
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
let future = w.run(&self.eng, &self.ring, completion);
|
||||||
|
self.async_jobs.set(
|
||||||
|
id,
|
||||||
|
AsyncJob {
|
||||||
|
_future: future,
|
||||||
|
work,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_completion(&self, id: CpuJobId) {
|
||||||
|
let cv = {
|
||||||
|
let mut exchange = self.completed_jobs.lock();
|
||||||
|
exchange.queue.push_back(id);
|
||||||
|
exchange.condvar.take()
|
||||||
|
};
|
||||||
|
if let Some(cv) = cv {
|
||||||
|
cv.notify_all();
|
||||||
|
}
|
||||||
|
if let Err(e) = uapi::eventfd_write(self.have_completed_jobs.raw(), 1) {
|
||||||
|
panic!("Could not signal job completion: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{AsyncCpuWork, CompletedWork, CpuJob, CpuWork, CpuWorker, WorkCompletion},
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
jay_async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
cpu_worker::{AsyncCpuWork, CompletedWork, CpuJob, CpuWork, CpuWorker, WorkCompletion},
|
jay_io_uring::IoUring,
|
||||||
io_uring::IoUring,
|
jay_utils::asyncevent::AsyncEvent,
|
||||||
utils::asyncevent::AsyncEvent,
|
jay_wheel::Wheel,
|
||||||
wheel::Wheel,
|
|
||||||
},
|
|
||||||
std::{future::pending, rc::Rc, sync::Arc},
|
std::{future::pending, rc::Rc, sync::Arc},
|
||||||
uapi::{OwnedFd, c::EFD_CLOEXEC},
|
uapi::{OwnedFd, c::EFD_CLOEXEC},
|
||||||
};
|
};
|
||||||
12
crates/criteria/Cargo.toml
Normal file
12
crates/criteria/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-criteria"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
ahash = "0.8.7"
|
||||||
|
linearize = { version = "0.1.3", features = ["derive"] }
|
||||||
|
regex = "1.11.1"
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::{
|
crate::{
|
||||||
CritMatcherId,
|
CritMatcherId,
|
||||||
crit_graph::{CritTarget, crit_upstream::CritUpstreamNode},
|
crit_graph::{CritTarget, crit_upstream::CritUpstreamNode},
|
||||||
},
|
},
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::{
|
crate::{
|
||||||
CritUpstreamNode,
|
CritUpstreamNode,
|
||||||
crit_graph::{
|
crit_graph::{
|
||||||
CritDownstream, CritDownstreamData, CritTarget, CritUpstreamData,
|
CritDownstream, CritDownstreamData, CritTarget, CritUpstreamData,
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::{
|
crate::{
|
||||||
CritMatcherId, CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
|
CritMatcherId, CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
|
||||||
crit_graph::{
|
crit_graph::{
|
||||||
CritTarget, CritUpstreamData,
|
CritTarget, CritUpstreamData,
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
criteria::{
|
CritDestroyListener, CritMatcherId, FixedRootMatcher, crit_leaf::CritLeafEvent,
|
||||||
CritDestroyListener, CritMatcherId, FixedRootMatcher, crit_leaf::CritLeafEvent,
|
crit_matchers::critm_constant::CritMatchConstant,
|
||||||
crit_matchers::critm_constant::CritMatchConstant,
|
|
||||||
},
|
|
||||||
utils::{copyhashmap::CopyHashMap, queue::AsyncQueue},
|
|
||||||
},
|
},
|
||||||
|
jay_utils::{copyhashmap::CopyHashMap, queue::AsyncQueue},
|
||||||
std::{
|
std::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
criteria::{
|
CritDestroyListener, CritMatcherId,
|
||||||
CritDestroyListener, CritMatcherId,
|
crit_graph::{
|
||||||
crit_graph::{
|
WeakCritTargetOwner,
|
||||||
WeakCritTargetOwner,
|
crit_downstream::CritDownstream,
|
||||||
crit_downstream::CritDownstream,
|
crit_target::{CritTarget, CritTargetOwner},
|
||||||
crit_target::{CritTarget, CritTargetOwner},
|
|
||||||
},
|
|
||||||
crit_per_target_data::CritPerTargetData,
|
|
||||||
},
|
},
|
||||||
utils::copyhashmap::CopyHashMap,
|
crit_per_target_data::CritPerTargetData,
|
||||||
},
|
},
|
||||||
|
jay_utils::copyhashmap::CopyHashMap,
|
||||||
std::{
|
std::{
|
||||||
cell::RefMut,
|
cell::RefMut,
|
||||||
mem,
|
mem,
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
criteria::{
|
CritUpstreamNode,
|
||||||
CritUpstreamNode,
|
crit_graph::{CritDownstream, CritDownstreamData, CritMgr, CritTarget},
|
||||||
crit_graph::{CritDownstream, CritDownstreamData, CritMgr, CritTarget},
|
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||||
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
|
||||||
},
|
|
||||||
utils::{cell_ext::CellExt, queue::AsyncQueue},
|
|
||||||
},
|
},
|
||||||
|
jay_utils::{cell_ext::CellExt, queue::AsyncQueue},
|
||||||
std::{
|
std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
|
|
@ -24,7 +22,7 @@ where
|
||||||
events: Rc<AsyncQueue<CritLeafEvent<Target>>>,
|
events: Rc<AsyncQueue<CritLeafEvent<Target>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::criteria) struct NodeHolder<Target>
|
pub struct NodeHolder<Target>
|
||||||
where
|
where
|
||||||
Target: CritTarget,
|
Target: CritTarget,
|
||||||
{
|
{
|
||||||
|
|
@ -77,7 +75,7 @@ impl<Target> CritLeafMatcher<Target>
|
||||||
where
|
where
|
||||||
Target: CritTarget,
|
Target: CritTarget,
|
||||||
{
|
{
|
||||||
pub(in crate::criteria) fn new(
|
pub(crate) fn new(
|
||||||
mgr: &Target::Mgr,
|
mgr: &Target::Mgr,
|
||||||
upstream: &Rc<dyn CritUpstreamNode<Target>>,
|
upstream: &Rc<dyn CritUpstreamNode<Target>>,
|
||||||
on_match: impl Fn(Target::LeafData) -> Box<dyn FnOnce()> + 'static,
|
on_match: impl Fn(Target::LeafData) -> Box<dyn FnOnce()> + 'static,
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
crate::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
||||||
std::{marker::PhantomData, rc::Rc},
|
std::{marker::PhantomData, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::{
|
crate::{
|
||||||
CritMatcherIds, FixedRootMatcher,
|
CritMatcherIds, FixedRootMatcher,
|
||||||
crit_graph::{
|
crit_graph::{
|
||||||
CritFixedRootCriterion, CritFixedRootCriterionBase, CritMgr, CritRoot, CritRootFixed,
|
CritFixedRootCriterion, CritFixedRootCriterionBase, CritMgr, CritRoot, CritRootFixed,
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
crate::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
||||||
std::{marker::PhantomData, rc::Rc},
|
std::{marker::PhantomData, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::{
|
crate::{
|
||||||
CritLiteralOrRegex, RootMatcherMap,
|
CritLiteralOrRegex, RootMatcherMap,
|
||||||
crit_graph::{CritRootCriterion, CritTarget},
|
crit_graph::{CritRootCriterion, CritTarget},
|
||||||
},
|
},
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::criteria::{
|
crate::{
|
||||||
CritMatcherId,
|
CritMatcherId,
|
||||||
crit_graph::{CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
crit_graph::{CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
||||||
},
|
},
|
||||||
|
|
@ -28,7 +28,7 @@ where
|
||||||
data: T,
|
data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) trait CritDestroyListenerBase<Target>: 'static
|
pub trait CritDestroyListenerBase<Target>: 'static
|
||||||
where
|
where
|
||||||
Target: CritTarget,
|
Target: CritTarget,
|
||||||
{
|
{
|
||||||
131
crates/criteria/src/lib.rs
Normal file
131
crates/criteria/src/lib.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
pub mod crit_graph;
|
||||||
|
pub mod crit_leaf;
|
||||||
|
pub mod crit_matchers;
|
||||||
|
pub mod crit_per_target_data;
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
crit_graph::{CritMgr, CritMiddle, CritRoot, CritRootCriterion, CritRootFixed},
|
||||||
|
crit_leaf::CritLeafMatcher,
|
||||||
|
crit_matchers::{critm_any_or_all::CritMatchAnyOrAll, critm_exactly::CritMatchExactly},
|
||||||
|
},
|
||||||
|
jay_utils::{copyhashmap::CopyHashMap, numcell::NumCell},
|
||||||
|
linearize::StaticMap,
|
||||||
|
regex::Regex,
|
||||||
|
std::rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
pub use {
|
||||||
|
crit_graph::{CritTarget, CritUpstreamNode},
|
||||||
|
crit_per_target_data::CritDestroyListener,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CritMatcherIds {
|
||||||
|
next: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CritMatcherIds {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next: NumCell::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CritMatcherIds {
|
||||||
|
pub fn next(&self) -> CritMatcherId {
|
||||||
|
CritMatcherId(self.next.fetch_add(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct CritMatcherId(u64);
|
||||||
|
|
||||||
|
impl CritMatcherId {
|
||||||
|
#[allow(clippy::allow_attributes, dead_code)]
|
||||||
|
pub fn raw(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::allow_attributes, dead_code)]
|
||||||
|
pub fn from_raw(id: u64) -> Self {
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CritMatcherId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RootMatcherMap<Target, T> = CopyHashMap<CritMatcherId, Weak<CritRoot<Target, T>>>;
|
||||||
|
pub type FixedRootMatcher<Target, T> =
|
||||||
|
StaticMap<bool, Rc<CritRoot<Target, CritRootFixed<Target, T>>>>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum CritLiteralOrRegex {
|
||||||
|
Literal(String),
|
||||||
|
Regex(Regex),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CritLiteralOrRegex {
|
||||||
|
fn matches(&self, string: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
CritLiteralOrRegex::Literal(p) => string == p,
|
||||||
|
CritLiteralOrRegex::Regex(r) => r.is_match(string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritMgrExt: CritMgr {
|
||||||
|
fn list(
|
||||||
|
&self,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
all: bool,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
if upstream.is_empty() {
|
||||||
|
return self.match_constant()[all].clone();
|
||||||
|
}
|
||||||
|
CritMiddle::new(self, upstream, CritMatchAnyOrAll::new(upstream, all))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exactly(
|
||||||
|
&self,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
num: usize,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
if num > upstream.len() {
|
||||||
|
return self.match_constant()[false].clone();
|
||||||
|
}
|
||||||
|
if num == 0 {
|
||||||
|
let upstream: Vec<_> = upstream.iter().map(|u| u.not(self)).collect();
|
||||||
|
return self.list(&upstream, true);
|
||||||
|
}
|
||||||
|
CritMiddle::new(self, upstream, CritMatchExactly::new(upstream, num))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leaf(
|
||||||
|
&self,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
on_match: impl Fn(<Self::Target as CritTarget>::LeafData) -> Box<dyn FnOnce()> + 'static,
|
||||||
|
) -> Rc<CritLeafMatcher<Self::Target>> {
|
||||||
|
CritLeafMatcher::new(self, upstream, on_match)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(
|
||||||
|
&self,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
upstream.not(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root<T>(&self, criterion: T) -> Rc<dyn CritUpstreamNode<Self::Target>>
|
||||||
|
where
|
||||||
|
T: CritRootCriterion<Self::Target>,
|
||||||
|
{
|
||||||
|
CritRoot::new(self.roots(), self.id(), criterion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CritMgrExt for T where T: CritMgr {}
|
||||||
10
crates/damage/Cargo.toml
Normal file
10
crates/damage/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-damage"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-geometry = { version = "0.1.0", path = "../geometry" }
|
||||||
|
jay-tree-types = { version = "0.1.0", path = "../tree-types" }
|
||||||
|
jay-units = { version = "0.1.0", path = "../units" }
|
||||||
116
crates/damage/src/lib.rs
Normal file
116
crates/damage/src/lib.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
use {
|
||||||
|
jay_geometry::Rect,
|
||||||
|
jay_tree_types::Transform,
|
||||||
|
jay_units::fixed::Fixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct DamageMatrix {
|
||||||
|
transform: Transform,
|
||||||
|
mx: f64,
|
||||||
|
my: f64,
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
smear: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DamageMatrix {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
transform: Default::default(),
|
||||||
|
mx: 1.0,
|
||||||
|
my: 1.0,
|
||||||
|
dx: 0.0,
|
||||||
|
dy: 0.0,
|
||||||
|
smear: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DamageMatrix {
|
||||||
|
pub fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
|
||||||
|
let x1 = rect.x1() - self.smear;
|
||||||
|
let x2 = rect.x2() + self.smear;
|
||||||
|
let y1 = rect.y1() - self.smear;
|
||||||
|
let y2 = rect.y2() + self.smear;
|
||||||
|
let [x1, y1, x2, y2] = match self.transform {
|
||||||
|
Transform::None => [x1, y1, x2, y2],
|
||||||
|
Transform::Rotate90 => [-y2, x1, -y1, x2],
|
||||||
|
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
|
||||||
|
Transform::Rotate270 => [y1, -x2, y2, -x1],
|
||||||
|
Transform::Flip => [-x2, y1, -x1, y2],
|
||||||
|
Transform::FlipRotate90 => [y1, x1, y2, x2],
|
||||||
|
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
|
||||||
|
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
|
||||||
|
};
|
||||||
|
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
|
||||||
|
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
|
||||||
|
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
|
||||||
|
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
|
||||||
|
Rect::new_saturating(x1, y1, x2, y2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
transform: Transform,
|
||||||
|
legacy_scale: i32,
|
||||||
|
buffer_width: i32,
|
||||||
|
buffer_height: i32,
|
||||||
|
viewport: Option<[Fixed; 4]>,
|
||||||
|
dst_width: i32,
|
||||||
|
dst_height: i32,
|
||||||
|
) -> DamageMatrix {
|
||||||
|
let mut buffer_width = buffer_width as f64;
|
||||||
|
let mut buffer_height = buffer_height as f64;
|
||||||
|
let dst_width = dst_width as f64;
|
||||||
|
let dst_height = dst_height as f64;
|
||||||
|
|
||||||
|
let mut mx = 1.0;
|
||||||
|
let mut my = 1.0;
|
||||||
|
if legacy_scale != 1 {
|
||||||
|
let scale_inv = 1.0 / (legacy_scale as f64);
|
||||||
|
mx = scale_inv;
|
||||||
|
my = scale_inv;
|
||||||
|
buffer_width *= scale_inv;
|
||||||
|
buffer_height *= scale_inv;
|
||||||
|
}
|
||||||
|
let (mut buffer_width, mut buffer_height) =
|
||||||
|
transform.maybe_swap((buffer_width, buffer_height));
|
||||||
|
let (mut dx, mut dy) = match transform {
|
||||||
|
Transform::None => (0.0, 0.0),
|
||||||
|
Transform::Rotate90 => (buffer_width, 0.0),
|
||||||
|
Transform::Rotate180 => (buffer_width, buffer_height),
|
||||||
|
Transform::Rotate270 => (0.0, buffer_height),
|
||||||
|
Transform::Flip => (buffer_width, 0.0),
|
||||||
|
Transform::FlipRotate90 => (0.0, 0.0),
|
||||||
|
Transform::FlipRotate180 => (0.0, buffer_height),
|
||||||
|
Transform::FlipRotate270 => (buffer_width, buffer_height),
|
||||||
|
};
|
||||||
|
if let Some([x, y, w, h]) = viewport {
|
||||||
|
dx -= x.to_f64();
|
||||||
|
dy -= y.to_f64();
|
||||||
|
buffer_width = w.to_f64();
|
||||||
|
buffer_height = h.to_f64();
|
||||||
|
}
|
||||||
|
let mut smear = false;
|
||||||
|
if dst_width != buffer_width {
|
||||||
|
let scale = dst_width / buffer_width;
|
||||||
|
mx *= scale;
|
||||||
|
dx *= scale;
|
||||||
|
smear |= dst_width > buffer_width;
|
||||||
|
}
|
||||||
|
if dst_height != buffer_height {
|
||||||
|
let scale = dst_height / buffer_height;
|
||||||
|
my *= scale;
|
||||||
|
dy *= scale;
|
||||||
|
smear |= dst_height > buffer_height;
|
||||||
|
}
|
||||||
|
DamageMatrix {
|
||||||
|
transform,
|
||||||
|
mx,
|
||||||
|
my,
|
||||||
|
dx,
|
||||||
|
dy,
|
||||||
|
smear: smear as _,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
crates/dbus-core/Cargo.toml
Normal file
14
crates/dbus-core/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-dbus-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-bufio = { version = "0.1.0", path = "../bufio" }
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "../io-uring" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
|
|
@ -3,10 +3,8 @@ use {
|
||||||
TY_ARRAY, TY_BOOLEAN, TY_BYTE, TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH,
|
TY_ARRAY, TY_BOOLEAN, TY_BYTE, TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH,
|
||||||
TY_SIGNATURE, TY_STRING, TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
|
TY_SIGNATURE, TY_STRING, TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
|
||||||
},
|
},
|
||||||
crate::{
|
crate::{types::Variant, DbusError, DynamicType, Parser},
|
||||||
dbus::{DbusError, DynamicType, Parser, types::Variant},
|
jay_utils::buf::DynamicBuf,
|
||||||
utils::buf::DynamicBuf,
|
|
||||||
},
|
|
||||||
std::ops::Deref,
|
std::ops::Deref,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -156,11 +154,8 @@ impl DynamicType {
|
||||||
}
|
}
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
{
|
{
|
||||||
let mut parser = Parser {
|
let mut parser =
|
||||||
buf: &parser.buf[..parser.pos + len],
|
Parser::new_at(&parser.buf[..parser.pos + len], parser.pos, parser.fds);
|
||||||
pos: parser.pos,
|
|
||||||
fds: parser.fds,
|
|
||||||
};
|
|
||||||
while !parser.eof() {
|
while !parser.eof() {
|
||||||
vals.push(el.parse(&mut parser)?);
|
vals.push(el.parse(&mut parser)?);
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{types::Variant, DbusType, Formatter},
|
||||||
dbus::{DbusType, Formatter, types::Variant},
|
jay_utils::buf::DynamicBuf,
|
||||||
utils::buf::DynamicBuf,
|
|
||||||
},
|
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
uapi::{OwnedFd, Packed},
|
uapi::{OwnedFd, Packed},
|
||||||
};
|
};
|
||||||
232
crates/dbus-core/src/lib.rs
Normal file
232
crates/dbus-core/src/lib.rs
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
pub use {property::{Get, GetReply}, types::*};
|
||||||
|
use {
|
||||||
|
jay_bufio::BufIoError,
|
||||||
|
jay_io_uring::IoUringError,
|
||||||
|
jay_utils::{buf::DynamicBuf, oserror::OsError},
|
||||||
|
std::{borrow::Cow, fmt::Display, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::OwnedFd,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod dynamic_type;
|
||||||
|
mod formatter;
|
||||||
|
mod parser;
|
||||||
|
pub mod property;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallError {
|
||||||
|
pub name: String,
|
||||||
|
pub msg: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CallError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(msg) = &self.msg {
|
||||||
|
write!(f, "{}: {}", self.name, msg)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DbusError {
|
||||||
|
#[error("Encountered an unknown type in a signature")]
|
||||||
|
UnknownType,
|
||||||
|
#[error("Function call reply does not contain a reply serial")]
|
||||||
|
NoReplySerial,
|
||||||
|
#[error("Signal message contains no interface or member or path")]
|
||||||
|
MissingSignalHeaders,
|
||||||
|
#[error("Method call message contains no interface or member or path")]
|
||||||
|
MissingMethodCallHeaders,
|
||||||
|
#[error("Error has no error name")]
|
||||||
|
NoErrorName,
|
||||||
|
#[error("The socket was killed")]
|
||||||
|
Killed,
|
||||||
|
#[error("{0}")]
|
||||||
|
CallError(CallError),
|
||||||
|
#[error("FD index is out of bounds")]
|
||||||
|
OobFds,
|
||||||
|
#[error("Variant has an invalid type")]
|
||||||
|
InvalidVariantType,
|
||||||
|
#[error("Could not create a socket")]
|
||||||
|
Socket(#[source] OsError),
|
||||||
|
#[error("Could not connect")]
|
||||||
|
Connect(#[source] IoUringError),
|
||||||
|
#[error("Could not write to the dbus socket")]
|
||||||
|
WriteError(#[source] IoUringError),
|
||||||
|
#[error("Could not read from the dbus socket")]
|
||||||
|
ReadError(#[source] IoUringError),
|
||||||
|
#[error("timeout")]
|
||||||
|
IoUringError(#[source] Box<IoUringError>),
|
||||||
|
#[error("Server did not send auth challenge")]
|
||||||
|
NoChallenge,
|
||||||
|
#[error("Server did not accept our authentication")]
|
||||||
|
Auth,
|
||||||
|
#[error("Array length is not a multiple of the element size")]
|
||||||
|
PodArrayLength,
|
||||||
|
#[error("Peer did not send enough fds")]
|
||||||
|
TooFewFds,
|
||||||
|
#[error("Variant signature is not a single type")]
|
||||||
|
TrailingVariantSignature,
|
||||||
|
#[error("Dict signature does not contain a terminating '}}'")]
|
||||||
|
UnterminatedDict,
|
||||||
|
#[error("Struct signature does not contain a terminating '}}'")]
|
||||||
|
UnterminatedStruct,
|
||||||
|
#[error("Dict signature contains trailing types")]
|
||||||
|
DictTrailing,
|
||||||
|
#[error("String does not contain valid UTF-8")]
|
||||||
|
InvalidUtf8,
|
||||||
|
#[error("Unexpected end of message")]
|
||||||
|
UnexpectedEof,
|
||||||
|
#[error("Boolean value was not 0 or 1")]
|
||||||
|
InvalidBoolValue,
|
||||||
|
#[error("Signature is empty")]
|
||||||
|
EmptySignature,
|
||||||
|
#[error("The session bus address is not set")]
|
||||||
|
SessionBusAddressNotSet,
|
||||||
|
#[error("Server does not support FD passing")]
|
||||||
|
UnixFd,
|
||||||
|
#[error("Server message has a different endianess than ourselves")]
|
||||||
|
InvalidEndianess,
|
||||||
|
#[error("Server speaks an unexpected protocol version")]
|
||||||
|
InvalidProtocol,
|
||||||
|
#[error("Signature contains an invalid type")]
|
||||||
|
InvalidSignatureType,
|
||||||
|
#[error("The signal already has a handler")]
|
||||||
|
AlreadyHandled,
|
||||||
|
#[error(transparent)]
|
||||||
|
BufIoError(#[from] BufIoError),
|
||||||
|
#[error(transparent)]
|
||||||
|
DbusError(Rc<DbusError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IoUringError> for DbusError {
|
||||||
|
fn from(e: IoUringError) -> Self {
|
||||||
|
DbusError::IoUringError(Box::new(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TY_BYTE: u8 = b'y';
|
||||||
|
const TY_BOOLEAN: u8 = b'b';
|
||||||
|
const TY_INT16: u8 = b'n';
|
||||||
|
const TY_UINT16: u8 = b'q';
|
||||||
|
const TY_INT32: u8 = b'i';
|
||||||
|
const TY_UINT32: u8 = b'u';
|
||||||
|
const TY_INT64: u8 = b'x';
|
||||||
|
const TY_UINT64: u8 = b't';
|
||||||
|
const TY_DOUBLE: u8 = b'd';
|
||||||
|
const TY_STRING: u8 = b's';
|
||||||
|
const TY_OBJECT_PATH: u8 = b'o';
|
||||||
|
const TY_SIGNATURE: u8 = b'g';
|
||||||
|
const TY_ARRAY: u8 = b'a';
|
||||||
|
const TY_VARIANT: u8 = b'v';
|
||||||
|
const TY_UNIX_FD: u8 = b'h';
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum DynamicType {
|
||||||
|
U8,
|
||||||
|
Bool,
|
||||||
|
I16,
|
||||||
|
U16,
|
||||||
|
I32,
|
||||||
|
U32,
|
||||||
|
I64,
|
||||||
|
U64,
|
||||||
|
F64,
|
||||||
|
String,
|
||||||
|
ObjectPath,
|
||||||
|
Signature,
|
||||||
|
Variant,
|
||||||
|
Fd,
|
||||||
|
Array(Box<DynamicType>),
|
||||||
|
DictEntry(Box<DynamicType>, Box<DynamicType>),
|
||||||
|
Struct(Vec<DynamicType>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser<'a> {
|
||||||
|
pub(crate) buf: &'a [u8],
|
||||||
|
pub(crate) pos: usize,
|
||||||
|
pub(crate) fds: &'a [Rc<OwnedFd>],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Formatter<'a> {
|
||||||
|
fds: &'a mut Vec<Rc<OwnedFd>>,
|
||||||
|
buf: &'a mut DynamicBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe trait Message<'a>: Sized + 'a {
|
||||||
|
const SIGNATURE: &'static str;
|
||||||
|
const INTERFACE: &'static str;
|
||||||
|
const MEMBER: &'static str;
|
||||||
|
type Generic<'b>: Message<'b>;
|
||||||
|
|
||||||
|
fn marshal(&self, w: &mut Formatter);
|
||||||
|
fn unmarshal(p: &mut Parser<'a>) -> Result<Self, DbusError>;
|
||||||
|
fn num_fds(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ErrorMessage<'a> {
|
||||||
|
pub msg: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a> Message<'a> for ErrorMessage<'a> {
|
||||||
|
const SIGNATURE: &'static str = "s";
|
||||||
|
const INTERFACE: &'static str = "";
|
||||||
|
const MEMBER: &'static str = "";
|
||||||
|
type Generic<'b> = ErrorMessage<'b>;
|
||||||
|
|
||||||
|
fn marshal(&self, w: &mut Formatter) {
|
||||||
|
self.msg.marshal(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmarshal(p: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||||
|
Ok(Self {
|
||||||
|
msg: p.unmarshal()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_fds(&self) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Property {
|
||||||
|
const INTERFACE: &'static str;
|
||||||
|
const PROPERTY: &'static str;
|
||||||
|
type Type: DbusType<'static>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Signal<'a>: Message<'a> {}
|
||||||
|
|
||||||
|
pub trait MethodCall<'a>: Message<'a> {
|
||||||
|
type Reply: Message<'static>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe trait DbusType<'a>: Clone + 'a {
|
||||||
|
const ALIGNMENT: usize;
|
||||||
|
const IS_POD: bool;
|
||||||
|
type Generic<'b>: DbusType<'b> + 'b;
|
||||||
|
|
||||||
|
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError>;
|
||||||
|
#[allow(clippy::allow_attributes, dead_code)]
|
||||||
|
fn write_signature(w: &mut Vec<u8>);
|
||||||
|
fn marshal(&self, fmt: &mut Formatter);
|
||||||
|
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError>;
|
||||||
|
|
||||||
|
fn num_fds(&self) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use {
|
||||||
|
super::{
|
||||||
|
DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, Signal,
|
||||||
|
types::{Bool, DictEntry, ObjectPath, Variant},
|
||||||
|
},
|
||||||
|
std::{borrow::Cow, rc::Rc},
|
||||||
|
uapi::OwnedFd,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::dbus::{
|
crate::{
|
||||||
|
types::{Bool, ObjectPath, Signature, Variant, FALSE, TRUE},
|
||||||
DbusError, DbusType, DynamicType, Parser,
|
DbusError, DbusType, DynamicType, Parser,
|
||||||
types::{Bool, FALSE, ObjectPath, Signature, TRUE, Variant},
|
|
||||||
},
|
},
|
||||||
bstr::ByteSlice,
|
bstr::ByteSlice,
|
||||||
std::{borrow::Cow, rc::Rc},
|
std::{borrow::Cow, rc::Rc},
|
||||||
|
|
@ -10,7 +10,11 @@ use {
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub fn new(buf: &'a [u8], fds: &'a [Rc<OwnedFd>]) -> Self {
|
pub fn new(buf: &'a [u8], fds: &'a [Rc<OwnedFd>]) -> Self {
|
||||||
Self { buf, pos: 0, fds }
|
Self::new_at(buf, 0, fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_at(buf: &'a [u8], pos: usize, fds: &'a [Rc<OwnedFd>]) -> Self {
|
||||||
|
Self { buf, pos, fds }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eof(&self) -> bool {
|
pub fn eof(&self) -> bool {
|
||||||
|
|
@ -107,11 +111,7 @@ impl<'a> Parser<'a> {
|
||||||
self.pos += len;
|
self.pos += len;
|
||||||
Ok(Cow::Borrowed(slice))
|
Ok(Cow::Borrowed(slice))
|
||||||
} else {
|
} else {
|
||||||
let mut parser = Parser {
|
let mut parser = Parser::new_at(&self.buf[..self.pos + len], self.pos, self.fds);
|
||||||
buf: &self.buf[..self.pos + len],
|
|
||||||
pos: self.pos,
|
|
||||||
fds: self.fds,
|
|
||||||
};
|
|
||||||
self.pos += len;
|
self.pos += len;
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
while !parser.eof() {
|
while !parser.eof() {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::dbus::{DbusError, DbusType, Formatter, Message, MethodCall, Parser},
|
crate::{DbusError, DbusType, Formatter, Message, MethodCall, Parser},
|
||||||
std::{borrow::Cow, marker::PhantomData},
|
std::{borrow::Cow, marker::PhantomData},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
dbus::{
|
DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE,
|
||||||
DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE,
|
TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING,
|
||||||
TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING,
|
TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
|
||||||
TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
|
|
||||||
},
|
|
||||||
utils::buf::DynamicBuf,
|
|
||||||
},
|
},
|
||||||
|
jay_utils::buf::DynamicBuf,
|
||||||
std::{borrow::Cow, ops::Deref, rc::Rc},
|
std::{borrow::Cow, ops::Deref, rc::Rc},
|
||||||
uapi::{OwnedFd, Packed, Pod},
|
uapi::{OwnedFd, Packed, Pod},
|
||||||
};
|
};
|
||||||
|
|
@ -501,31 +499,6 @@ impl<'a> Variant<'a> {
|
||||||
w.push(c);
|
w.push(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn borrow<'b>(&'b self) -> Variant<'b> {
|
|
||||||
match self {
|
|
||||||
Variant::U8(v) => Variant::U8(*v),
|
|
||||||
Variant::Bool(v) => Variant::Bool(*v),
|
|
||||||
Variant::I16(v) => Variant::I16(*v),
|
|
||||||
Variant::U16(v) => Variant::U16(*v),
|
|
||||||
Variant::I32(v) => Variant::I32(*v),
|
|
||||||
Variant::U32(v) => Variant::U32(*v),
|
|
||||||
Variant::I64(v) => Variant::I64(*v),
|
|
||||||
Variant::U64(v) => Variant::U64(*v),
|
|
||||||
Variant::F64(v) => Variant::F64(*v),
|
|
||||||
Variant::String(v) => Variant::String(v.deref().into()),
|
|
||||||
Variant::ObjectPath(v) => Variant::ObjectPath(ObjectPath(v.0.deref().into())),
|
|
||||||
Variant::Signature(v) => Variant::Signature(Signature(v.0.deref().into())),
|
|
||||||
Variant::Variant(v) => Variant::Variant(Box::new(v.deref().borrow())),
|
|
||||||
Variant::Fd(v) => Variant::Fd(v.clone()),
|
|
||||||
Variant::Array(t, v) => {
|
|
||||||
Variant::Array(t.clone(), v.iter().map(|v| v.borrow()).collect())
|
|
||||||
}
|
|
||||||
Variant::DictEntry(k, v) => {
|
|
||||||
Variant::DictEntry(Box::new(k.deref().borrow()), Box::new(v.deref().borrow()))
|
|
||||||
}
|
|
||||||
Variant::Struct(v) => Variant::Struct(v.iter().map(|v| v.borrow()).collect()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a> DbusType<'a> for Variant<'a> {
|
unsafe impl<'a> DbusType<'a> for Variant<'a> {
|
||||||
13
crates/drm-feedback/Cargo.toml
Normal file
13
crates/drm-feedback/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-drm-feedback"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-video-types = { version = "0.1.0", path = "../video-types" }
|
||||||
|
|
||||||
|
ahash = "0.8.7"
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
149
crates/drm-feedback/src/lib.rs
Normal file
149
crates/drm-feedback/src/lib.rs
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
use {
|
||||||
|
ahash::AHashMap,
|
||||||
|
byteorder::{NativeEndian, WriteBytesExt},
|
||||||
|
jay_video_types::Modifier,
|
||||||
|
std::{io::Write, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{OwnedFd, c},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrmFeedbackIds {
|
||||||
|
next: std::cell::Cell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DrmFeedbackIds {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next: std::cell::Cell::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrmFeedbackIds {
|
||||||
|
pub fn next(&self) -> DrmFeedbackId {
|
||||||
|
let id = self.next.get();
|
||||||
|
self.next.set(id + 1);
|
||||||
|
DrmFeedbackId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub struct DrmFeedbackId(u64);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrmFeedbackShared {
|
||||||
|
pub fd: Rc<OwnedFd>,
|
||||||
|
pub size: usize,
|
||||||
|
pub main_device: c::dev_t,
|
||||||
|
pub indices: AHashMap<(u32, Modifier), u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrmFeedback {
|
||||||
|
pub id: DrmFeedbackId,
|
||||||
|
pub shared: Rc<DrmFeedbackShared>,
|
||||||
|
pub tranches: Vec<DrmFeedbackTranche>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DrmFeedbackTranche {
|
||||||
|
pub device: c::dev_t,
|
||||||
|
pub indices: Vec<u16>,
|
||||||
|
pub scanout: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrmFeedback {
|
||||||
|
pub fn new<C: DrmFeedbackContext + ?Sized>(
|
||||||
|
ids: &DrmFeedbackIds,
|
||||||
|
render_ctx: &C,
|
||||||
|
) -> Result<Self, DrmFeedbackError> {
|
||||||
|
let main_device = match render_ctx.main_device() {
|
||||||
|
Some(dev) => dev,
|
||||||
|
_ => return Err(DrmFeedbackError::NoDrmDevice),
|
||||||
|
};
|
||||||
|
let (data, index_map) = create_fd_data(render_ctx);
|
||||||
|
let mut memfd =
|
||||||
|
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
|
||||||
|
memfd.write_all(&data).unwrap();
|
||||||
|
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap();
|
||||||
|
uapi::fcntl_add_seals(
|
||||||
|
memfd.raw(),
|
||||||
|
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ok(Self {
|
||||||
|
id: ids.next(),
|
||||||
|
tranches: vec![DrmFeedbackTranche {
|
||||||
|
device: main_device,
|
||||||
|
indices: (0..index_map.len()).map(|v| v as u16).collect(),
|
||||||
|
scanout: false,
|
||||||
|
}],
|
||||||
|
shared: Rc::new(DrmFeedbackShared {
|
||||||
|
fd: Rc::new(memfd),
|
||||||
|
size: data.len(),
|
||||||
|
main_device,
|
||||||
|
indices: index_map,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_scanout(
|
||||||
|
&self,
|
||||||
|
ids: &DrmFeedbackIds,
|
||||||
|
devnum: c::dev_t,
|
||||||
|
formats: &[(u32, Modifier)],
|
||||||
|
) -> Result<Option<Self>, DrmFeedbackError> {
|
||||||
|
let mut tranches = vec![];
|
||||||
|
{
|
||||||
|
let mut indices = vec![];
|
||||||
|
for (format, modifier) in formats {
|
||||||
|
if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) {
|
||||||
|
indices.push(*idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if indices.len() > 0 {
|
||||||
|
tranches.push(DrmFeedbackTranche {
|
||||||
|
device: devnum,
|
||||||
|
indices,
|
||||||
|
scanout: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tranches.extend(self.tranches.iter().cloned());
|
||||||
|
Ok(Some(Self {
|
||||||
|
id: ids.next(),
|
||||||
|
shared: self.shared.clone(),
|
||||||
|
tranches,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DrmFeedbackContext {
|
||||||
|
fn main_device(&self) -> Option<c::dev_t>;
|
||||||
|
fn for_each_read_format(&self, f: &mut dyn FnMut(u32, Modifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_fd_data<C: DrmFeedbackContext + ?Sized>(
|
||||||
|
ctx: &C,
|
||||||
|
) -> (Vec<u8>, AHashMap<(u32, Modifier), u16>) {
|
||||||
|
let mut vec = vec![];
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
let mut pos = 0;
|
||||||
|
ctx.for_each_read_format(&mut |format, modifier| {
|
||||||
|
vec.write_u32::<NativeEndian>(format).unwrap();
|
||||||
|
vec.write_u32::<NativeEndian>(0).unwrap();
|
||||||
|
vec.write_u64::<NativeEndian>(modifier).unwrap();
|
||||||
|
map.insert((format, modifier), pos);
|
||||||
|
pos += 1;
|
||||||
|
});
|
||||||
|
(vec, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DrmFeedbackError {
|
||||||
|
#[error("Graphics API does not have a DRM device")]
|
||||||
|
NoDrmDevice,
|
||||||
|
}
|
||||||
11
crates/edid/Cargo.toml
Normal file
11
crates/edid/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-edid"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
description = "EDID parsing for Jay"
|
||||||
|
repository = "https://github.com/mahkoh/jay"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
||||||
|
thiserror = "2.0.11"
|
||||||
1312
crates/edid/src/lib.rs
Normal file
1312
crates/edid/src/lib.rs
Normal file
File diff suppressed because it is too large
Load diff
14
crates/eventfd-cache/Cargo.toml
Normal file
14
crates/eventfd-cache/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-eventfd-cache"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-async-engine = { version = "0.1.0", path = "../async-engine" }
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "../io-uring" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
log = { version = "0.4.20", features = ["std"] }
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
157
crates/eventfd-cache/src/lib.rs
Normal file
157
crates/eventfd-cache/src/lib.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
use {
|
||||||
|
jay_async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
|
jay_io_uring::{IoUring, IoUringError},
|
||||||
|
jay_utils::{
|
||||||
|
buf::Buf,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
||||||
|
queue::AsyncQueue,
|
||||||
|
stack::Stack,
|
||||||
|
},
|
||||||
|
std::{cell::Cell, future::poll_fn, pin::Pin, rc::Rc, slice, task::Poll},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{OwnedFd, c},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EventfdError {
|
||||||
|
#[error("Could not create an eventfd")]
|
||||||
|
CreateEventfd(#[source] OsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventfdCache {
|
||||||
|
inner: Rc<Inner>,
|
||||||
|
_task: SpawnedFuture<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
fds: Stack<Rc<OwnedFd>>,
|
||||||
|
recycle: AsyncQueue<Rc<OwnedFd>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Eventfd {
|
||||||
|
cache: Rc<Inner>,
|
||||||
|
pub fd: Rc<OwnedFd>,
|
||||||
|
signaled: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventfdCache {
|
||||||
|
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Rc<Self> {
|
||||||
|
let inner = Rc::new(Inner {
|
||||||
|
ring: ring.clone(),
|
||||||
|
fds: Default::default(),
|
||||||
|
recycle: Default::default(),
|
||||||
|
});
|
||||||
|
let task = eng.spawn("eventfd-cache", inner.clone().recycle());
|
||||||
|
Rc::new(Self { inner, _task: task })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire(&self) -> Result<Eventfd, EventfdError> {
|
||||||
|
let fd = match self.inner.fds.pop() {
|
||||||
|
Some(fd) => fd,
|
||||||
|
_ => uapi::eventfd(0, c::EFD_CLOEXEC)
|
||||||
|
.map(Rc::new)
|
||||||
|
.map_os_err(EventfdError::CreateEventfd)?,
|
||||||
|
};
|
||||||
|
Ok(Eventfd {
|
||||||
|
cache: self.inner.clone(),
|
||||||
|
fd,
|
||||||
|
signaled: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eventfd {
|
||||||
|
pub fn is_signaled(&self) -> bool {
|
||||||
|
self.signaled.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signaled(&self) -> Result<(), IoUringError> {
|
||||||
|
if self.signaled.get() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.cache.ring.readable(&self.fd).await?;
|
||||||
|
self.signaled.set(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signaled_blocking(&self) -> Result<(), OsError> {
|
||||||
|
if self.signaled.get() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut pollfd = c::pollfd {
|
||||||
|
fd: self.fd.raw(),
|
||||||
|
events: c::POLLIN,
|
||||||
|
revents: 0,
|
||||||
|
};
|
||||||
|
uapi::poll(slice::from_mut(&mut pollfd), -1).to_os_error()?;
|
||||||
|
self.signaled.set(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
async fn recycle(self: Rc<Self>) {
|
||||||
|
let slf = &*self;
|
||||||
|
let mut fds = vec![];
|
||||||
|
let mut bufs = vec![];
|
||||||
|
let mut tasks = vec![];
|
||||||
|
let mut todo = vec![];
|
||||||
|
loop {
|
||||||
|
fds.clear();
|
||||||
|
tasks.clear();
|
||||||
|
todo.clear();
|
||||||
|
slf.recycle.non_empty().await;
|
||||||
|
while let Some(fd) = slf.recycle.try_pop() {
|
||||||
|
fds.push(fd);
|
||||||
|
}
|
||||||
|
for (idx, fd) in fds.iter().enumerate() {
|
||||||
|
if idx >= bufs.len() {
|
||||||
|
bufs.push(Buf::new(size_of::<u64>()));
|
||||||
|
}
|
||||||
|
let fd = fd.clone();
|
||||||
|
let buf = bufs[idx].clone();
|
||||||
|
tasks.push(async move { slf.ring.read(&fd, buf).await });
|
||||||
|
todo.push(idx);
|
||||||
|
}
|
||||||
|
poll_fn(|ctx| {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < todo.len() {
|
||||||
|
let idx = todo[i];
|
||||||
|
let task = unsafe { Pin::new_unchecked(&mut tasks[idx]) };
|
||||||
|
if let Poll::Ready(res) = task.poll(ctx) {
|
||||||
|
todo.swap_remove(i);
|
||||||
|
match res {
|
||||||
|
Ok(_) => {
|
||||||
|
self.fds.push(fds[idx].clone());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not read from eventfd: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if todo.is_empty() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Eventfd {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.signaled.get() {
|
||||||
|
self.cache.recycle.push(self.fd.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::EventfdCache,
|
||||||
async_engine::AsyncEngine, eventfd_cache::EventfdCache, io_uring::IoUring, utils::array,
|
jay_async_engine::AsyncEngine,
|
||||||
},
|
jay_io_uring::IoUring,
|
||||||
|
jay_utils::array,
|
||||||
std::{rc::Rc, slice},
|
std::{rc::Rc, slice},
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
13
crates/formats/Cargo.toml
Normal file
13
crates/formats/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-formats"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
description = "Pixel format tables for Jay"
|
||||||
|
repository = "https://github.com/mahkoh/jay"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ahash = "0.8.7"
|
||||||
|
ash = { package = "jay-ash", version = "0.3.0" }
|
||||||
|
clap = { version = "4.4.18", features = ["derive", "wrap_help"] }
|
||||||
|
jay-config = { version = "1.10.0", path = "../jay-config" }
|
||||||
559
crates/formats/src/lib.rs
Normal file
559
crates/formats/src/lib.rs
Normal file
|
|
@ -0,0 +1,559 @@
|
||||||
|
use {
|
||||||
|
ahash::AHashMap,
|
||||||
|
ash::vk,
|
||||||
|
clap::{ValueEnum, builder::PossibleValue},
|
||||||
|
jay_config::video::Format as ConfigFormat,
|
||||||
|
std::{
|
||||||
|
fmt::{self, Debug, Write},
|
||||||
|
sync::LazyLock,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type GLenum = u32;
|
||||||
|
pub type GLint = i32;
|
||||||
|
|
||||||
|
const GL_RGBA: GLint = 0x1908;
|
||||||
|
const GL_RGBA8: GLenum = 0x8058;
|
||||||
|
const GL_BGRA_EXT: GLint = 0x80E1;
|
||||||
|
const GL_UNSIGNED_BYTE: GLint = 0x1401;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct FormatShmInfo {
|
||||||
|
pub gl_format: GLint,
|
||||||
|
pub gl_internal_format: GLenum,
|
||||||
|
pub gl_type: GLint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Format {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub vk_format: vk::Format,
|
||||||
|
pub drm: u32,
|
||||||
|
pub wl_id: Option<u32>,
|
||||||
|
pub external_only_guess: bool,
|
||||||
|
pub has_alpha: bool,
|
||||||
|
pub opaque: Option<&'static Format>,
|
||||||
|
pub shm_info: Option<FormatShmInfo>,
|
||||||
|
pub config: ConfigFormat,
|
||||||
|
pub bpp: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn default(config: ConfigFormat) -> Format {
|
||||||
|
Format {
|
||||||
|
name: "",
|
||||||
|
vk_format: vk::Format::UNDEFINED,
|
||||||
|
drm: 0,
|
||||||
|
wl_id: None,
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: false,
|
||||||
|
opaque: None,
|
||||||
|
shm_info: None,
|
||||||
|
config,
|
||||||
|
bpp: 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Format {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.drm == other.drm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Format {}
|
||||||
|
|
||||||
|
impl ValueEnum for &'static Format {
|
||||||
|
fn value_variants<'a>() -> &'a [Self] {
|
||||||
|
ref_formats()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_possible_value(&self) -> Option<PossibleValue> {
|
||||||
|
Some(PossibleValue::new(self.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FORMATS_MAP: LazyLock<AHashMap<u32, &'static Format>> = LazyLock::new(|| {
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
for format in FORMATS {
|
||||||
|
assert!(map.insert(format.drm, format).is_none());
|
||||||
|
}
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
static FORMATS_REFS: LazyLock<Vec<&'static Format>> = LazyLock::new(|| FORMATS.iter().collect());
|
||||||
|
|
||||||
|
static FORMATS_NAMES: LazyLock<AHashMap<&'static str, &'static Format>> = LazyLock::new(|| {
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
for format in FORMATS {
|
||||||
|
assert!(map.insert(format.name, format).is_none());
|
||||||
|
}
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
static FORMATS_CONFIG: LazyLock<AHashMap<ConfigFormat, &'static Format>> = LazyLock::new(|| {
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
for format in FORMATS {
|
||||||
|
assert!(map.insert(format.config, format).is_none());
|
||||||
|
}
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_dont_panic() {
|
||||||
|
formats();
|
||||||
|
named_formats();
|
||||||
|
config_formats();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn formats() -> &'static AHashMap<u32, &'static Format> {
|
||||||
|
&FORMATS_MAP
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ref_formats() -> &'static [&'static Format] {
|
||||||
|
&FORMATS_REFS
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn named_formats() -> &'static AHashMap<&'static str, &'static Format> {
|
||||||
|
&FORMATS_NAMES
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_formats() -> &'static AHashMap<ConfigFormat, &'static Format> {
|
||||||
|
&FORMATS_CONFIG
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
|
||||||
|
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug(fourcc: u32) -> impl Debug {
|
||||||
|
fmt::from_fn(move |fmt| {
|
||||||
|
fmt.write_char(fourcc as u8 as char)?;
|
||||||
|
fmt.write_char((fourcc >> 8) as u8 as char)?;
|
||||||
|
fmt.write_char((fourcc >> 16) as u8 as char)?;
|
||||||
|
fmt.write_char((fourcc >> 24) as u8 as char)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const ARGB8888_ID: u32 = 0;
|
||||||
|
const ARGB8888_DRM: u32 = fourcc_code('A', 'R', '2', '4');
|
||||||
|
|
||||||
|
const XRGB8888_ID: u32 = 1;
|
||||||
|
const XRGB8888_DRM: u32 = fourcc_code('X', 'R', '2', '4');
|
||||||
|
|
||||||
|
pub fn map_wayland_format_id(id: u32) -> u32 {
|
||||||
|
match id {
|
||||||
|
ARGB8888_ID => ARGB8888_DRM,
|
||||||
|
XRGB8888_ID => XRGB8888_DRM,
|
||||||
|
_ => id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static ARGB8888: &Format = &Format {
|
||||||
|
name: "argb8888",
|
||||||
|
shm_info: Some(FormatShmInfo {
|
||||||
|
gl_format: GL_BGRA_EXT,
|
||||||
|
gl_internal_format: GL_RGBA8,
|
||||||
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
}),
|
||||||
|
vk_format: vk::Format::B8G8R8A8_UNORM,
|
||||||
|
bpp: 4,
|
||||||
|
drm: ARGB8888_DRM,
|
||||||
|
wl_id: Some(ARGB8888_ID),
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XRGB8888),
|
||||||
|
config: ConfigFormat::ARGB8888,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static XRGB8888: &Format = &Format {
|
||||||
|
name: "xrgb8888",
|
||||||
|
shm_info: Some(FormatShmInfo {
|
||||||
|
gl_format: GL_BGRA_EXT,
|
||||||
|
gl_internal_format: GL_RGBA8,
|
||||||
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
}),
|
||||||
|
vk_format: vk::Format::B8G8R8A8_UNORM,
|
||||||
|
bpp: 4,
|
||||||
|
drm: XRGB8888_DRM,
|
||||||
|
wl_id: Some(XRGB8888_ID),
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: false,
|
||||||
|
opaque: None,
|
||||||
|
config: ConfigFormat::XRGB8888,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ABGR8888: &Format = &Format {
|
||||||
|
name: "abgr8888",
|
||||||
|
shm_info: Some(FormatShmInfo {
|
||||||
|
gl_format: GL_RGBA,
|
||||||
|
gl_internal_format: GL_RGBA8,
|
||||||
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
}),
|
||||||
|
vk_format: vk::Format::R8G8B8A8_UNORM,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('A', 'B', '2', '4'),
|
||||||
|
wl_id: None,
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XBGR8888),
|
||||||
|
config: ConfigFormat::ABGR8888,
|
||||||
|
};
|
||||||
|
|
||||||
|
static XBGR8888: &Format = &Format {
|
||||||
|
name: "xbgr8888",
|
||||||
|
shm_info: Some(FormatShmInfo {
|
||||||
|
gl_format: GL_RGBA,
|
||||||
|
gl_internal_format: GL_RGBA8,
|
||||||
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
}),
|
||||||
|
vk_format: vk::Format::R8G8B8A8_UNORM,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('X', 'B', '2', '4'),
|
||||||
|
wl_id: None,
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: false,
|
||||||
|
opaque: None,
|
||||||
|
config: ConfigFormat::XBGR8888,
|
||||||
|
};
|
||||||
|
|
||||||
|
static R8: &Format = &Format {
|
||||||
|
name: "r8",
|
||||||
|
vk_format: vk::Format::R8_UNORM,
|
||||||
|
bpp: 1,
|
||||||
|
drm: fourcc_code('R', '8', ' ', ' '),
|
||||||
|
..default(ConfigFormat::R8)
|
||||||
|
};
|
||||||
|
|
||||||
|
static GR88: &Format = &Format {
|
||||||
|
name: "gr88",
|
||||||
|
vk_format: vk::Format::R8G8_UNORM,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('G', 'R', '8', '8'),
|
||||||
|
..default(ConfigFormat::GR88)
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGB888: &Format = &Format {
|
||||||
|
name: "rgb888",
|
||||||
|
vk_format: vk::Format::B8G8R8_UNORM,
|
||||||
|
bpp: 3,
|
||||||
|
drm: fourcc_code('R', 'G', '2', '4'),
|
||||||
|
..default(ConfigFormat::RGB888)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGR888: &Format = &Format {
|
||||||
|
name: "bgr888",
|
||||||
|
vk_format: vk::Format::R8G8B8_UNORM,
|
||||||
|
bpp: 3,
|
||||||
|
drm: fourcc_code('B', 'G', '2', '4'),
|
||||||
|
..default(ConfigFormat::BGR888)
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGBA4444: &Format = &Format {
|
||||||
|
name: "rgba4444",
|
||||||
|
vk_format: vk::Format::R4G4B4A4_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('R', 'A', '1', '2'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(RGBX4444),
|
||||||
|
..default(ConfigFormat::RGBA4444)
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGBX4444: &Format = &Format {
|
||||||
|
name: "rgbx4444",
|
||||||
|
vk_format: vk::Format::R4G4B4A4_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('R', 'X', '1', '2'),
|
||||||
|
..default(ConfigFormat::RGBX4444)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGRA4444: &Format = &Format {
|
||||||
|
name: "bgra4444",
|
||||||
|
vk_format: vk::Format::B4G4R4A4_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('B', 'A', '1', '2'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(BGRX4444),
|
||||||
|
..default(ConfigFormat::BGRA4444)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGRX4444: &Format = &Format {
|
||||||
|
name: "bgrx4444",
|
||||||
|
vk_format: vk::Format::B4G4R4A4_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('B', 'X', '1', '2'),
|
||||||
|
..default(ConfigFormat::BGRX4444)
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGB565: &Format = &Format {
|
||||||
|
name: "rgb565",
|
||||||
|
vk_format: vk::Format::R5G6B5_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('R', 'G', '1', '6'),
|
||||||
|
..default(ConfigFormat::RGB565)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGR565: &Format = &Format {
|
||||||
|
name: "bgr565",
|
||||||
|
vk_format: vk::Format::B5G6R5_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('B', 'G', '1', '6'),
|
||||||
|
..default(ConfigFormat::BGR565)
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGBA5551: &Format = &Format {
|
||||||
|
name: "rgba5551",
|
||||||
|
vk_format: vk::Format::R5G5B5A1_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('R', 'A', '1', '5'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(RGBX5551),
|
||||||
|
..default(ConfigFormat::RGBA5551)
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGBX5551: &Format = &Format {
|
||||||
|
name: "rgbx5551",
|
||||||
|
vk_format: vk::Format::R5G5B5A1_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('R', 'X', '1', '5'),
|
||||||
|
..default(ConfigFormat::RGBX5551)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGRA5551: &Format = &Format {
|
||||||
|
name: "bgra5551",
|
||||||
|
vk_format: vk::Format::B5G5R5A1_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('B', 'A', '1', '5'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(BGRX5551),
|
||||||
|
..default(ConfigFormat::BGRA5551)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGRX5551: &Format = &Format {
|
||||||
|
name: "bgrx5551",
|
||||||
|
vk_format: vk::Format::B5G5R5A1_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('B', 'X', '1', '5'),
|
||||||
|
..default(ConfigFormat::BGRX5551)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ARGB1555: &Format = &Format {
|
||||||
|
name: "argb1555",
|
||||||
|
vk_format: vk::Format::A1R5G5B5_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('A', 'R', '1', '5'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XRGB1555),
|
||||||
|
..default(ConfigFormat::ARGB1555)
|
||||||
|
};
|
||||||
|
|
||||||
|
static XRGB1555: &Format = &Format {
|
||||||
|
name: "xrgb1555",
|
||||||
|
vk_format: vk::Format::A1R5G5B5_UNORM_PACK16,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('X', 'R', '1', '5'),
|
||||||
|
..default(ConfigFormat::XRGB1555)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ARGB2101010: &Format = &Format {
|
||||||
|
name: "argb2101010",
|
||||||
|
vk_format: vk::Format::A2R10G10B10_UNORM_PACK32,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('A', 'R', '3', '0'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XRGB2101010),
|
||||||
|
..default(ConfigFormat::ARGB2101010)
|
||||||
|
};
|
||||||
|
|
||||||
|
static XRGB2101010: &Format = &Format {
|
||||||
|
name: "xrgb2101010",
|
||||||
|
vk_format: vk::Format::A2R10G10B10_UNORM_PACK32,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('X', 'R', '3', '0'),
|
||||||
|
..default(ConfigFormat::XRGB2101010)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ABGR2101010: &Format = &Format {
|
||||||
|
name: "abgr2101010",
|
||||||
|
vk_format: vk::Format::A2B10G10R10_UNORM_PACK32,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('A', 'B', '3', '0'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XBGR2101010),
|
||||||
|
..default(ConfigFormat::ABGR2101010)
|
||||||
|
};
|
||||||
|
|
||||||
|
static XBGR2101010: &Format = &Format {
|
||||||
|
name: "xbgr2101010",
|
||||||
|
vk_format: vk::Format::A2B10G10R10_UNORM_PACK32,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('X', 'B', '3', '0'),
|
||||||
|
..default(ConfigFormat::XBGR2101010)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ABGR16161616: &Format = &Format {
|
||||||
|
name: "abgr16161616",
|
||||||
|
vk_format: vk::Format::R16G16B16A16_UNORM,
|
||||||
|
bpp: 8,
|
||||||
|
drm: fourcc_code('A', 'B', '4', '8'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XBGR16161616),
|
||||||
|
..default(ConfigFormat::ABGR16161616)
|
||||||
|
};
|
||||||
|
|
||||||
|
static XBGR16161616: &Format = &Format {
|
||||||
|
name: "xbgr16161616",
|
||||||
|
vk_format: vk::Format::R16G16B16A16_UNORM,
|
||||||
|
bpp: 8,
|
||||||
|
drm: fourcc_code('X', 'B', '4', '8'),
|
||||||
|
..default(ConfigFormat::XBGR16161616)
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static ABGR16161616F: &Format = &Format {
|
||||||
|
name: "abgr16161616f",
|
||||||
|
vk_format: vk::Format::R16G16B16A16_SFLOAT,
|
||||||
|
bpp: 8,
|
||||||
|
drm: fourcc_code('A', 'B', '4', 'H'),
|
||||||
|
has_alpha: true,
|
||||||
|
opaque: Some(XBGR16161616F),
|
||||||
|
..default(ConfigFormat::ABGR16161616F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static XBGR16161616F: &Format = &Format {
|
||||||
|
name: "xbgr16161616f",
|
||||||
|
vk_format: vk::Format::R16G16B16A16_SFLOAT,
|
||||||
|
bpp: 8,
|
||||||
|
drm: fourcc_code('X', 'B', '4', 'H'),
|
||||||
|
..default(ConfigFormat::XBGR16161616F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGR161616: &Format = &Format {
|
||||||
|
name: "bgr161616",
|
||||||
|
vk_format: vk::Format::R16G16B16_UNORM,
|
||||||
|
bpp: 6,
|
||||||
|
drm: fourcc_code('B', 'G', '4', '8'),
|
||||||
|
..default(ConfigFormat::BGR161616)
|
||||||
|
};
|
||||||
|
|
||||||
|
static R16F: &Format = &Format {
|
||||||
|
name: "r16f",
|
||||||
|
vk_format: vk::Format::R16_SFLOAT,
|
||||||
|
bpp: 2,
|
||||||
|
drm: fourcc_code('R', ' ', ' ', 'H'),
|
||||||
|
..default(ConfigFormat::R16F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static GR1616F: &Format = &Format {
|
||||||
|
name: "gr1616f",
|
||||||
|
vk_format: vk::Format::R16G16_SFLOAT,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('G', 'R', ' ', 'H'),
|
||||||
|
..default(ConfigFormat::GR1616F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGR161616F: &Format = &Format {
|
||||||
|
name: "bgr161616f",
|
||||||
|
vk_format: vk::Format::R16G16B16_SFLOAT,
|
||||||
|
bpp: 6,
|
||||||
|
drm: fourcc_code('B', 'G', 'R', 'H'),
|
||||||
|
..default(ConfigFormat::BGR161616F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static R32F: &Format = &Format {
|
||||||
|
name: "r32f",
|
||||||
|
vk_format: vk::Format::R32_SFLOAT,
|
||||||
|
bpp: 4,
|
||||||
|
drm: fourcc_code('R', ' ', ' ', 'F'),
|
||||||
|
..default(ConfigFormat::R32F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static GR3232F: &Format = &Format {
|
||||||
|
name: "gr3232f",
|
||||||
|
vk_format: vk::Format::R32G32_SFLOAT,
|
||||||
|
bpp: 8,
|
||||||
|
drm: fourcc_code('G', 'R', ' ', 'F'),
|
||||||
|
..default(ConfigFormat::GR3232F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static BGR323232F: &Format = &Format {
|
||||||
|
name: "bgr323232f",
|
||||||
|
vk_format: vk::Format::R32G32B32_SFLOAT,
|
||||||
|
bpp: 12,
|
||||||
|
drm: fourcc_code('B', 'G', 'R', 'F'),
|
||||||
|
..default(ConfigFormat::BGR323232F)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ABGR32323232F: &Format = &Format {
|
||||||
|
name: "abgr32323232f",
|
||||||
|
vk_format: vk::Format::R32G32B32A32_SFLOAT,
|
||||||
|
bpp: 16,
|
||||||
|
drm: fourcc_code('A', 'B', '8', 'F'),
|
||||||
|
has_alpha: true,
|
||||||
|
..default(ConfigFormat::ABGR32323232F)
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static FORMATS: &[Format] = &[
|
||||||
|
*ARGB8888,
|
||||||
|
*XRGB8888,
|
||||||
|
*ABGR8888,
|
||||||
|
*XBGR8888,
|
||||||
|
*R8,
|
||||||
|
*GR88,
|
||||||
|
*RGB888,
|
||||||
|
*BGR888,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*RGBA4444,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*RGBX4444,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGRA4444,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGRX4444,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*RGB565,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGR565,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*RGBA5551,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*RGBX5551,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGRA5551,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGRX5551,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*ARGB1555,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*XRGB1555,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*ARGB2101010,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*XRGB2101010,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*ABGR2101010,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*XBGR2101010,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*ABGR16161616,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*XBGR16161616,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*ABGR16161616F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*XBGR16161616F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGR161616,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*R16F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*GR1616F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGR161616F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*R32F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*GR3232F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*BGR323232F,
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
*ABGR32323232F,
|
||||||
|
];
|
||||||
11
crates/geometry/Cargo.toml
Normal file
11
crates/geometry/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-geometry"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
description = "Geometry primitives for Jay"
|
||||||
|
repository = "https://github.com/mahkoh/jay"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-algorithms = { version = "0.4.0", path = "../algorithms" }
|
||||||
|
smallvec = { version = "1.11.1", features = ["const_generics", "const_new", "union"] }
|
||||||
365
crates/geometry/src/lib.rs
Normal file
365
crates/geometry/src/lib.rs
Normal file
|
|
@ -0,0 +1,365 @@
|
||||||
|
mod region;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub use region::{DamageQueue, RegionBuilder};
|
||||||
|
use {
|
||||||
|
jay_algorithms::rect::{NoTag, RectRaw, Tag},
|
||||||
|
smallvec::SmallVec,
|
||||||
|
std::fmt::{Debug, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Rect<T = NoTag>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
raw: RectRaw<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
||||||
|
pub struct Region<T = NoTag>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
rects: SmallVec<[RectRaw<T>; 1]>,
|
||||||
|
extents: Rect,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Rect {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Debug::fmt(&self.raw, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||||
|
pub struct RectOverflow {
|
||||||
|
pub left: i32,
|
||||||
|
pub right: i32,
|
||||||
|
pub top: i32,
|
||||||
|
pub bottom: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectOverflow {
|
||||||
|
pub fn is_contained(&self) -> bool {
|
||||||
|
self.left <= 0 && self.right <= 0 && self.top <= 0 && self.bottom <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x_overflow(&self) -> bool {
|
||||||
|
self.left > 0 || self.right > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y_overflow(&self) -> bool {
|
||||||
|
self.top > 0 || self.bottom > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Rect<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
pub fn untag(&self) -> Rect {
|
||||||
|
Rect {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: self.raw.x1,
|
||||||
|
y1: self.raw.y1,
|
||||||
|
x2: self.raw.x2,
|
||||||
|
y2: self.raw.y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rect {
|
||||||
|
pub fn new_empty(x: i32, y: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: x,
|
||||||
|
y1: y,
|
||||||
|
x2: x,
|
||||||
|
y2: y,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(x1: i32, y1: i32, x2: i32, y2: i32) -> Option<Self> {
|
||||||
|
if x2 < x1 || y2 < y1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(test), expect(dead_code))]
|
||||||
|
fn new_unchecked_danger(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_sized(x1: i32, y1: i32, width: i32, height: i32) -> Option<Self> {
|
||||||
|
if width < 0 || height < 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Self::new(x1, y1, x1 + width, y1 + height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_saturating(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2: x2.max(x1),
|
||||||
|
y2: y2.max(y1),
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_sized_saturating(x1: i32, y1: i32, width: i32, height: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2: x1.saturating_add(width.max(0)),
|
||||||
|
y2: y1.saturating_add(height.max(0)),
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(&self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: self.raw.x1.min(other.raw.x1),
|
||||||
|
y1: self.raw.y1.min(other.raw.y1),
|
||||||
|
x2: self.raw.x2.max(other.raw.x2),
|
||||||
|
y2: self.raw.y2.max(other.raw.y2),
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(&self, other: Self) -> Self {
|
||||||
|
let x1 = self.raw.x1.max(other.raw.x1);
|
||||||
|
let y1 = self.raw.y1.max(other.raw.y1);
|
||||||
|
let x2 = self.raw.x2.min(other.raw.x2).max(x1);
|
||||||
|
let y2 = self.raw.y2.min(other.raw.y2).max(y1);
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_size_saturating(&self, width: i32, height: i32) -> Self {
|
||||||
|
Self::new_sized_saturating(self.raw.x1, self.raw.y1, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_tag(&self, tag: u32) -> Rect<u32> {
|
||||||
|
Rect {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: self.raw.x1,
|
||||||
|
y1: self.raw.y1,
|
||||||
|
x2: self.raw.x2,
|
||||||
|
y2: self.raw.y2,
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Rect<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
#[cfg_attr(not(test), expect(dead_code))]
|
||||||
|
fn new_unchecked_danger_tagged(x1: i32, y1: i32, x2: i32, y2: i32, tag: T) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersects(&self, other: &Self) -> bool {
|
||||||
|
self.raw.x1 < other.raw.x2
|
||||||
|
&& other.raw.x1 < self.raw.x2
|
||||||
|
&& self.raw.y1 < other.raw.y2
|
||||||
|
&& other.raw.y1 < self.raw.y2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, x: i32, y: i32) -> bool {
|
||||||
|
self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not_contains(&self, x: i32, y: i32) -> bool {
|
||||||
|
!self.contains(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dist_squared(&self, x: i32, y: i32) -> i128 {
|
||||||
|
let x = x as i64;
|
||||||
|
let y = y as i64;
|
||||||
|
let x1 = self.raw.x1 as i64;
|
||||||
|
let x2 = self.raw.x2 as i64;
|
||||||
|
let y1 = self.raw.y1 as i64;
|
||||||
|
let y2 = self.raw.y2 as i64;
|
||||||
|
let mut dx = 0;
|
||||||
|
if x1 > x {
|
||||||
|
dx = x1 - x;
|
||||||
|
} else if x2 < x {
|
||||||
|
dx = x - x2;
|
||||||
|
}
|
||||||
|
let mut dy = 0;
|
||||||
|
if y1 > y {
|
||||||
|
dy = y1 - y;
|
||||||
|
} else if y2 < y {
|
||||||
|
dy = y - y2;
|
||||||
|
}
|
||||||
|
let dx = dx as i128;
|
||||||
|
let dy = dy as i128;
|
||||||
|
dx * dx + dy * dy
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_rect<U>(&self, rect: &Rect<U>) -> bool
|
||||||
|
where
|
||||||
|
U: Tag,
|
||||||
|
{
|
||||||
|
self.raw.x1 <= rect.raw.x1
|
||||||
|
&& self.raw.y1 <= rect.raw.x1
|
||||||
|
&& rect.raw.x2 <= self.raw.x2
|
||||||
|
&& rect.raw.y2 <= self.raw.y2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_overflow<U>(&self, child: &Rect<U>) -> RectOverflow
|
||||||
|
where
|
||||||
|
U: Tag,
|
||||||
|
{
|
||||||
|
RectOverflow {
|
||||||
|
left: self.raw.x1 - child.raw.x1,
|
||||||
|
right: child.raw.x2 - self.raw.x2,
|
||||||
|
top: self.raw.y1 - child.raw.y1,
|
||||||
|
bottom: child.raw.y2 - self.raw.y2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_origin(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: self.raw.x2 - self.raw.x1,
|
||||||
|
y2: self.raw.y2 - self.raw.y1,
|
||||||
|
tag: self.raw.tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_(&self, dx: i32, dy: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: self.raw.x1.saturating_add(dx),
|
||||||
|
y1: self.raw.y1.saturating_add(dy),
|
||||||
|
x2: self.raw.x2.saturating_add(dx),
|
||||||
|
y2: self.raw.y2.saturating_add(dy),
|
||||||
|
tag: self.raw.tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_point(&self, x1: i32, y1: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2: x1 + self.raw.x2 - self.raw.x1,
|
||||||
|
y2: y1 + self.raw.y2 - self.raw.y1,
|
||||||
|
tag: self.raw.tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(&self, x: i32, y: i32) -> (i32, i32) {
|
||||||
|
(x.wrapping_sub(self.raw.x1), y.wrapping_sub(self.raw.y1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate_inv(&self, x: i32, y: i32) -> (i32, i32) {
|
||||||
|
(x.wrapping_add(self.raw.x1), y.wrapping_add(self.raw.y1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x1(&self) -> i32 {
|
||||||
|
self.raw.x1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x2(&self) -> i32 {
|
||||||
|
self.raw.x2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y1(&self) -> i32 {
|
||||||
|
self.raw.y1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y2(&self) -> i32 {
|
||||||
|
self.raw.y2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> i32 {
|
||||||
|
self.raw.x2 - self.raw.x1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> i32 {
|
||||||
|
self.raw.y2 - self.raw.y1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> (i32, i32) {
|
||||||
|
(self.raw.x1, self.raw.y1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> (i32, i32) {
|
||||||
|
(self.width(), self.height())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center(&self) -> (i32, i32) {
|
||||||
|
(
|
||||||
|
self.raw.x1 + self.width() / 2,
|
||||||
|
self.raw.y1 + self.height() / 2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tag(&self) -> T {
|
||||||
|
self.raw.tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{Rect, Region},
|
||||||
rect::{Rect, Region},
|
|
||||||
utils::{
|
|
||||||
array,
|
|
||||||
ptr_ext::{MutPtrExt, PtrExt},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
jay_algorithms::rect::{
|
jay_algorithms::rect::{
|
||||||
RectRaw, Tag,
|
RectRaw, Tag,
|
||||||
region::{
|
region::{
|
||||||
|
|
@ -15,6 +9,7 @@ use {
|
||||||
},
|
},
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
std::{
|
std::{
|
||||||
|
array,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::UnsafeCell,
|
cell::UnsafeCell,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
|
@ -176,7 +171,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
|
||||||
pub fn extents(&self) -> Rect {
|
pub fn extents(&self) -> Rect {
|
||||||
self.extents
|
self.extents
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +268,6 @@ impl RegionBuilder {
|
||||||
self.base.clone()
|
self.base.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.pending.clear();
|
self.pending.clear();
|
||||||
self.base = Region::empty();
|
self.base = Region::empty();
|
||||||
|
|
@ -321,26 +314,26 @@ impl DamageQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn damage(&self, rects: &[Rect]) {
|
pub fn damage(&self, rects: &[Rect]) {
|
||||||
let datas = unsafe { self.datas.get().deref_mut() };
|
let datas = unsafe { &mut *self.datas.get() };
|
||||||
for data in datas {
|
for data in datas {
|
||||||
data.extend(rects);
|
data.extend(rects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
let data = unsafe { &mut self.datas.get().deref_mut()[self.this] };
|
let data = unsafe { &mut (&mut *self.datas.get())[self.this] };
|
||||||
data.clear();
|
data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_all(&self) {
|
pub fn clear_all(&self) {
|
||||||
let datas = unsafe { self.datas.get().deref_mut() };
|
let datas = unsafe { &mut *self.datas.get() };
|
||||||
for data in datas {
|
for data in datas {
|
||||||
data.clear();
|
data.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> Region {
|
pub fn get(&self) -> Region {
|
||||||
let data = unsafe { &self.datas.get().deref()[self.this] };
|
let data = unsafe { &(&*self.datas.get())[self.this] };
|
||||||
Region::from_rects2(data)
|
Region::from_rects2(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::rect::{Rect, Region},
|
crate::{Rect, Region},
|
||||||
jay_algorithms::rect::{NoTag, RectRaw},
|
jay_algorithms::rect::{NoTag, RectRaw},
|
||||||
};
|
};
|
||||||
|
|
||||||
8
crates/gfx-types/Cargo.toml
Normal file
8
crates/gfx-types/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-gfx-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
uapi = "0.2.13"
|
||||||
38
crates/gfx-types/src/lib.rs
Normal file
38
crates/gfx-types/src/lib.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use {
|
||||||
|
std::{cell::Cell, error::Error, rc::Rc},
|
||||||
|
uapi::OwnedFd,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||||
|
pub enum AlphaMode {
|
||||||
|
#[default]
|
||||||
|
PremultipliedElectrical,
|
||||||
|
PremultipliedOptical,
|
||||||
|
Straight,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ShmMemory {
|
||||||
|
fn len(&self) -> usize;
|
||||||
|
fn safe_access(&self) -> ShmMemoryBacking;
|
||||||
|
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ShmMemoryBacking {
|
||||||
|
Ptr(*const [Cell<u8>]),
|
||||||
|
Fd(Rc<OwnedFd>, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShmMemory for Vec<Cell<u8>> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn safe_access(&self) -> ShmMemoryBacking {
|
||||||
|
ShmMemoryBacking::Ptr(&**self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
|
||||||
|
f(self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
14
crates/input-types/Cargo.toml
Normal file
14
crates/input-types/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-input-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
description = "Input data types for the Jay compositor"
|
||||||
|
repository = "https://github.com/mahkoh/jay"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-output-types = { version = "0.1.0", path = "../output-types" }
|
||||||
|
jay-units = { version = "0.1.0", path = "../units" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
linearize = { version = "0.1.3", features = ["derive"] }
|
||||||
482
crates/input-types/src/lib.rs
Normal file
482
crates/input-types/src/lib.rs
Normal file
|
|
@ -0,0 +1,482 @@
|
||||||
|
use {
|
||||||
|
jay_output_types::ConnectorId,
|
||||||
|
jay_units::Fixed,
|
||||||
|
jay_utils::{numcell::NumCell, static_text::StaticText},
|
||||||
|
linearize::Linearize,
|
||||||
|
std::{
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
ops::{BitOr, BitOrAssign},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! linear_ids {
|
||||||
|
($ids:ident, $id:ident $(,)?) => {
|
||||||
|
linear_ids!($ids, $id, u32);
|
||||||
|
};
|
||||||
|
($ids:ident, $id:ident, $ty:ty $(,)?) => {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct $ids {
|
||||||
|
next: NumCell<$ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for $ids {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next: NumCell::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $ids {
|
||||||
|
pub fn next(&self) -> $id {
|
||||||
|
$id(self.next.fetch_add(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct $id($ty);
|
||||||
|
|
||||||
|
impl $id {
|
||||||
|
pub fn raw(&self) -> $ty {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_raw(id: $ty) -> Self {
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for $id {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Linearize)]
|
||||||
|
pub enum InputDeviceAccelProfile {
|
||||||
|
Flat,
|
||||||
|
Adaptive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for InputDeviceAccelProfile {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
InputDeviceAccelProfile::Flat => "Flat",
|
||||||
|
InputDeviceAccelProfile::Adaptive => "Adaptive",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Linearize)]
|
||||||
|
pub enum InputDeviceClickMethod {
|
||||||
|
None,
|
||||||
|
ButtonAreas,
|
||||||
|
Clickfinger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for InputDeviceClickMethod {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
InputDeviceClickMethod::None => "none",
|
||||||
|
InputDeviceClickMethod::ButtonAreas => "button-areas",
|
||||||
|
InputDeviceClickMethod::Clickfinger => "clickfinger",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Linearize)]
|
||||||
|
pub enum InputDeviceCapability {
|
||||||
|
Keyboard,
|
||||||
|
Pointer,
|
||||||
|
Touch,
|
||||||
|
TabletTool,
|
||||||
|
TabletPad,
|
||||||
|
Gesture,
|
||||||
|
Switch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for InputDeviceCapability {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
InputDeviceCapability::Keyboard => "keyboard",
|
||||||
|
InputDeviceCapability::Pointer => "pointer",
|
||||||
|
InputDeviceCapability::Touch => "touch",
|
||||||
|
InputDeviceCapability::TabletTool => "tablet tool",
|
||||||
|
InputDeviceCapability::TabletPad => "tablet pad",
|
||||||
|
InputDeviceCapability::Gesture => "gesture",
|
||||||
|
InputDeviceCapability::Switch => "switch",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(InputDeviceGroupIds, InputDeviceGroupId, usize);
|
||||||
|
linear_ids!(InputDeviceIds, InputDeviceId);
|
||||||
|
|
||||||
|
pub type TransformMatrix = [[f64; 2]; 2];
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum KeyState {
|
||||||
|
Released,
|
||||||
|
Pressed,
|
||||||
|
Repeated,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum ButtonState {
|
||||||
|
Released,
|
||||||
|
Pressed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Linearize)]
|
||||||
|
pub enum ScrollAxis {
|
||||||
|
Vertical = 0,
|
||||||
|
Horizontal = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum AxisSource {
|
||||||
|
Wheel,
|
||||||
|
Finger,
|
||||||
|
Continuous,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const AXIS_120: i32 = 120;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
|
||||||
|
pub struct Leds(pub u32);
|
||||||
|
|
||||||
|
pub const LED_NUM_LOCK: Leds = Leds(1 << 0);
|
||||||
|
pub const LED_CAPS_LOCK: Leds = Leds(1 << 1);
|
||||||
|
pub const LED_SCROLL_LOCK: Leds = Leds(1 << 2);
|
||||||
|
pub const LED_COMPOSE: Leds = Leds(1 << 3);
|
||||||
|
pub const LED_KANA: Leds = Leds(1 << 4);
|
||||||
|
|
||||||
|
impl Leds {
|
||||||
|
pub const fn none() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn raw(self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for Leds {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 | rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign for Leds {
|
||||||
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
|
self.0 |= rhs.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(TabletIds, TabletId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TabletInit {
|
||||||
|
pub id: TabletId,
|
||||||
|
pub group: InputDeviceGroupId,
|
||||||
|
pub name: String,
|
||||||
|
pub pid: u32,
|
||||||
|
pub vid: u32,
|
||||||
|
pub bustype: Option<u32>,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(TabletToolIds, TabletToolId, usize);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TabletToolInit {
|
||||||
|
pub tablet_id: TabletId,
|
||||||
|
pub id: TabletToolId,
|
||||||
|
pub type_: TabletToolType,
|
||||||
|
pub hardware_serial: u64,
|
||||||
|
pub hardware_id_wacom: u64,
|
||||||
|
pub capabilities: Vec<TabletToolCapability>,
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(TabletPadIds, TabletPadId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TabletPadInit {
|
||||||
|
pub id: TabletPadId,
|
||||||
|
pub group: InputDeviceGroupId,
|
||||||
|
pub path: String,
|
||||||
|
pub buttons: u32,
|
||||||
|
pub strips: u32,
|
||||||
|
pub rings: u32,
|
||||||
|
pub dials: u32,
|
||||||
|
pub groups: Vec<TabletPadGroupInit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TabletPadGroupInit {
|
||||||
|
pub buttons: Vec<u32>,
|
||||||
|
pub rings: Vec<u32>,
|
||||||
|
pub strips: Vec<u32>,
|
||||||
|
pub dials: Vec<u32>,
|
||||||
|
pub modes: u32,
|
||||||
|
pub mode: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum PadButtonState {
|
||||||
|
Released,
|
||||||
|
Pressed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ToolButtonState {
|
||||||
|
Released,
|
||||||
|
Pressed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum TabletToolType {
|
||||||
|
Pen,
|
||||||
|
Eraser,
|
||||||
|
Brush,
|
||||||
|
Pencil,
|
||||||
|
Airbrush,
|
||||||
|
Finger,
|
||||||
|
Mouse,
|
||||||
|
Lens,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum TabletToolCapability {
|
||||||
|
Tilt,
|
||||||
|
Pressure,
|
||||||
|
Distance,
|
||||||
|
Rotation,
|
||||||
|
Slider,
|
||||||
|
Wheel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum TabletRingEventSource {
|
||||||
|
Finger,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum TabletStripEventSource {
|
||||||
|
Finger,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TabletToolChanges {
|
||||||
|
pub down: Option<bool>,
|
||||||
|
pub pos: Option<TabletTool2dChange<TabletToolPositionChange>>,
|
||||||
|
pub pressure: Option<f64>,
|
||||||
|
pub distance: Option<f64>,
|
||||||
|
pub tilt: Option<TabletTool2dChange<f64>>,
|
||||||
|
pub rotation: Option<f64>,
|
||||||
|
pub slider: Option<f64>,
|
||||||
|
pub wheel: Option<TabletToolWheelChange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct TabletTool2dChange<T> {
|
||||||
|
pub x: T,
|
||||||
|
pub y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct TabletToolPositionChange {
|
||||||
|
pub x: f64,
|
||||||
|
pub dx: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct TabletToolWheelChange {
|
||||||
|
pub degrees: f64,
|
||||||
|
pub clicks: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
|
pub enum SwitchEvent {
|
||||||
|
LidOpened,
|
||||||
|
LidClosed,
|
||||||
|
ConvertedToLaptop,
|
||||||
|
ConvertedToTablet,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InputEvent {
|
||||||
|
Key {
|
||||||
|
time_usec: u64,
|
||||||
|
key: u32,
|
||||||
|
state: KeyState,
|
||||||
|
},
|
||||||
|
ConnectorPosition {
|
||||||
|
time_usec: u64,
|
||||||
|
connector: ConnectorId,
|
||||||
|
x: Fixed,
|
||||||
|
y: Fixed,
|
||||||
|
},
|
||||||
|
Motion {
|
||||||
|
time_usec: u64,
|
||||||
|
dx: Fixed,
|
||||||
|
dy: Fixed,
|
||||||
|
dx_unaccelerated: Fixed,
|
||||||
|
dy_unaccelerated: Fixed,
|
||||||
|
},
|
||||||
|
MotionAbsolute {
|
||||||
|
time_usec: u64,
|
||||||
|
x_normed: f32,
|
||||||
|
y_normed: f32,
|
||||||
|
},
|
||||||
|
Button {
|
||||||
|
time_usec: u64,
|
||||||
|
button: u32,
|
||||||
|
state: ButtonState,
|
||||||
|
},
|
||||||
|
|
||||||
|
AxisPx {
|
||||||
|
dist: Fixed,
|
||||||
|
axis: ScrollAxis,
|
||||||
|
inverted: bool,
|
||||||
|
},
|
||||||
|
AxisSource {
|
||||||
|
source: AxisSource,
|
||||||
|
},
|
||||||
|
AxisStop {
|
||||||
|
axis: ScrollAxis,
|
||||||
|
},
|
||||||
|
Axis120 {
|
||||||
|
dist: i32,
|
||||||
|
axis: ScrollAxis,
|
||||||
|
inverted: bool,
|
||||||
|
},
|
||||||
|
AxisFrame {
|
||||||
|
time_usec: u64,
|
||||||
|
},
|
||||||
|
SwipeBegin {
|
||||||
|
time_usec: u64,
|
||||||
|
finger_count: u32,
|
||||||
|
},
|
||||||
|
SwipeUpdate {
|
||||||
|
time_usec: u64,
|
||||||
|
dx: Fixed,
|
||||||
|
dy: Fixed,
|
||||||
|
dx_unaccelerated: Fixed,
|
||||||
|
dy_unaccelerated: Fixed,
|
||||||
|
},
|
||||||
|
SwipeEnd {
|
||||||
|
time_usec: u64,
|
||||||
|
cancelled: bool,
|
||||||
|
},
|
||||||
|
PinchBegin {
|
||||||
|
time_usec: u64,
|
||||||
|
finger_count: u32,
|
||||||
|
},
|
||||||
|
PinchUpdate {
|
||||||
|
time_usec: u64,
|
||||||
|
dx: Fixed,
|
||||||
|
dy: Fixed,
|
||||||
|
dx_unaccelerated: Fixed,
|
||||||
|
dy_unaccelerated: Fixed,
|
||||||
|
scale: Fixed,
|
||||||
|
rotation: Fixed,
|
||||||
|
},
|
||||||
|
PinchEnd {
|
||||||
|
time_usec: u64,
|
||||||
|
cancelled: bool,
|
||||||
|
},
|
||||||
|
HoldBegin {
|
||||||
|
time_usec: u64,
|
||||||
|
finger_count: u32,
|
||||||
|
},
|
||||||
|
HoldEnd {
|
||||||
|
time_usec: u64,
|
||||||
|
cancelled: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
SwitchEvent {
|
||||||
|
time_usec: u64,
|
||||||
|
event: SwitchEvent,
|
||||||
|
},
|
||||||
|
|
||||||
|
TabletToolAdded {
|
||||||
|
time_usec: u64,
|
||||||
|
init: Box<TabletToolInit>,
|
||||||
|
},
|
||||||
|
TabletToolChanged {
|
||||||
|
time_usec: u64,
|
||||||
|
id: TabletToolId,
|
||||||
|
changes: Box<TabletToolChanges>,
|
||||||
|
},
|
||||||
|
TabletToolButton {
|
||||||
|
time_usec: u64,
|
||||||
|
id: TabletToolId,
|
||||||
|
button: u32,
|
||||||
|
state: ToolButtonState,
|
||||||
|
},
|
||||||
|
TabletToolRemoved {
|
||||||
|
time_usec: u64,
|
||||||
|
id: TabletToolId,
|
||||||
|
},
|
||||||
|
|
||||||
|
TabletPadButton {
|
||||||
|
time_usec: u64,
|
||||||
|
id: TabletPadId,
|
||||||
|
button: u32,
|
||||||
|
state: PadButtonState,
|
||||||
|
},
|
||||||
|
TabletPadModeSwitch {
|
||||||
|
time_usec: u64,
|
||||||
|
pad: TabletPadId,
|
||||||
|
group: u32,
|
||||||
|
mode: u32,
|
||||||
|
},
|
||||||
|
TabletPadRing {
|
||||||
|
time_usec: u64,
|
||||||
|
pad: TabletPadId,
|
||||||
|
ring: u32,
|
||||||
|
source: Option<TabletRingEventSource>,
|
||||||
|
angle: Option<f64>,
|
||||||
|
},
|
||||||
|
TabletPadStrip {
|
||||||
|
time_usec: u64,
|
||||||
|
pad: TabletPadId,
|
||||||
|
strip: u32,
|
||||||
|
source: Option<TabletStripEventSource>,
|
||||||
|
position: Option<f64>,
|
||||||
|
},
|
||||||
|
TabletPadDial {
|
||||||
|
time_usec: u64,
|
||||||
|
pad: TabletPadId,
|
||||||
|
dial: u32,
|
||||||
|
value120: i32,
|
||||||
|
},
|
||||||
|
TouchDown {
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
x_normed: Fixed,
|
||||||
|
y_normed: Fixed,
|
||||||
|
},
|
||||||
|
TouchUp {
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
},
|
||||||
|
TouchMotion {
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
x_normed: Fixed,
|
||||||
|
y_normed: Fixed,
|
||||||
|
},
|
||||||
|
TouchCancel {
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
},
|
||||||
|
TouchFrame {
|
||||||
|
time_usec: u64,
|
||||||
|
},
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue