Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 3540cdc4be |
771 changed files with 21508 additions and 30279 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
|
@ -1,3 +1,3 @@
|
||||||
[submodule "crates/toml-config/toml-test"]
|
[submodule "toml-config/toml-test"]
|
||||||
path = crates/toml-config/toml-test
|
path = toml-config/toml-test
|
||||||
url = https://github.com/mahkoh/toml-tests.git
|
url = https://github.com/mahkoh/toml-tests.git
|
||||||
|
|
|
||||||
499
Cargo.lock
generated
499
Cargo.lock
generated
|
|
@ -625,21 +625,11 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-algorithms"
|
name = "jay-algorithms"
|
||||||
version = "1.12.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-allocator"
|
|
||||||
version = "1.12.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"
|
||||||
|
|
@ -649,52 +639,6 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-async-engine"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-time",
|
|
||||||
"jay-tracy",
|
|
||||||
"jay-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-bufio"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-utils",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-bugs"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-clientmem"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-cpu-worker",
|
|
||||||
"jay-gfx-types",
|
|
||||||
"jay-tracy",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-cmm"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-compositor"
|
name = "jay-compositor"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
|
|
@ -720,48 +664,9 @@ 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",
|
||||||
|
|
@ -777,11 +682,13 @@ 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",
|
||||||
|
|
@ -789,9 +696,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-config"
|
name = "jay-config"
|
||||||
version = "1.12.0"
|
version = "1.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
|
"bincode",
|
||||||
"bstr",
|
"bstr",
|
||||||
"error_reporter",
|
"error_reporter",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
|
@ -803,409 +711,24 @@ dependencies = [
|
||||||
"uapi",
|
"uapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-config-schema"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"jay-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-cpu-worker"
|
|
||||||
version = "1.12.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 = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"jay-utils",
|
|
||||||
"linearize",
|
|
||||||
"regex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-damage"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-geometry",
|
|
||||||
"jay-tree-types",
|
|
||||||
"jay-units",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-dbus-core"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"bstr",
|
|
||||||
"jay-bufio",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-utils",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-drm-feedback"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"byteorder",
|
|
||||||
"jay-video-types",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-edid"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"bstr",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-eventfd-cache"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-async-engine",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-formats"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"clap",
|
|
||||||
"jay-ash",
|
|
||||||
"jay-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-geometry"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-algorithms",
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-gfx-types"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-input-types"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-output-types",
|
|
||||||
"jay-units",
|
|
||||||
"jay-utils",
|
|
||||||
"linearize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-io-uring"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-async-engine",
|
|
||||||
"jay-time",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"run-on-drop",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-keyboard"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"blake3",
|
|
||||||
"jay-input-types",
|
|
||||||
"jay-utils",
|
|
||||||
"kbvm",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-layout-animation"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-geometry",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-libinput"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"bstr",
|
|
||||||
"cc",
|
|
||||||
"isnt 0.2.0",
|
|
||||||
"jay-utils",
|
|
||||||
"libloading",
|
|
||||||
"log",
|
|
||||||
"repc",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-logger"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"bstr",
|
|
||||||
"clap",
|
|
||||||
"dirs",
|
|
||||||
"humantime",
|
|
||||||
"jay-config",
|
|
||||||
"jay-utils",
|
|
||||||
"linearize",
|
|
||||||
"log",
|
|
||||||
"parking_lot",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-output-schedule"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"futures-util",
|
|
||||||
"jay-async-engine",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-output-types"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"blake3",
|
|
||||||
"jay-cmm",
|
|
||||||
"jay-formats",
|
|
||||||
"jay-utils",
|
|
||||||
"linearize",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-pango"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"jay-geometry",
|
|
||||||
"repc",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-pr-caps"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-utils",
|
|
||||||
"opera",
|
|
||||||
"parking_lot",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-sighand"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-async-engine",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-theme"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-cmm",
|
|
||||||
"jay-config",
|
|
||||||
"jay-gfx-types",
|
|
||||||
"jay-utils",
|
|
||||||
"linearize",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-time"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-toml"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"bstr",
|
|
||||||
"indexmap",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-toml-config"
|
name = "jay-toml-config"
|
||||||
version = "1.12.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bstr",
|
"bstr",
|
||||||
"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 = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"parking_lot",
|
|
||||||
"rustc-demangle",
|
|
||||||
"tracy-client-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-tree-types"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-config",
|
|
||||||
"jay-utils",
|
|
||||||
"linearize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-udmabuf"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-allocator",
|
|
||||||
"jay-formats",
|
|
||||||
"jay-utils",
|
|
||||||
"jay-video-types",
|
|
||||||
"log",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-units"
|
|
||||||
version = "1.12.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-utils"
|
|
||||||
version = "1.12.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 = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"arrayvec",
|
|
||||||
"jay-formats",
|
|
||||||
"jay-utils",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-wheel"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"jay-async-engine",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-time",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-wire-buf"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"bstr",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-time",
|
|
||||||
"jay-units",
|
|
||||||
"jay-utils",
|
|
||||||
"jay-wire-types",
|
|
||||||
"smallvec",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-wire-types"
|
|
||||||
version = "1.12.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jay-xcon"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"bstr",
|
|
||||||
"jay-bufio",
|
|
||||||
"jay-io-uring",
|
|
||||||
"jay-utils",
|
|
||||||
"log",
|
|
||||||
"thiserror",
|
|
||||||
"uapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1999,7 +1522,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml-spec"
|
name = "toml-spec"
|
||||||
version = "1.12.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"error_reporter",
|
"error_reporter",
|
||||||
|
|
@ -2411,7 +1934,7 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wire-to-xml"
|
name = "wire-to-xml"
|
||||||
version = "1.12.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
@ -2528,7 +2051,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-to-wire"
|
name = "xml-to-wire"
|
||||||
version = "1.12.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
||||||
113
Cargo.toml
113
Cargo.toml
|
|
@ -1,9 +1,9 @@
|
||||||
[package]
|
[package]
|
||||||
name = "jay-compositor"
|
name = "jay-compositor"
|
||||||
version.workspace = true
|
version = "1.12.0"
|
||||||
edition.workspace = true
|
edition = "2024"
|
||||||
build = "build/build.rs"
|
build = "build/build.rs"
|
||||||
license.workspace = true
|
license = "GPL-3.0-only"
|
||||||
description = "The Jay compositor"
|
description = "The Jay compositor"
|
||||||
repository = "https://github.com/mahkoh/jay"
|
repository = "https://github.com/mahkoh/jay"
|
||||||
default-run = "jay"
|
default-run = "jay"
|
||||||
|
|
@ -13,61 +13,7 @@ name = "jay"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
members = ["jay-config", "toml-config", "algorithms", "toml-spec", "wire-to-xml", "xml-to-wire"]
|
||||||
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",
|
|
||||||
]
|
|
||||||
|
|
||||||
[workspace.package]
|
|
||||||
version = "1.12.0"
|
|
||||||
edition = "2024"
|
|
||||||
license = "GPL-3.0-only"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
@ -77,48 +23,9 @@ debug = "full"
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jay-config = { path = "crates/jay-config" }
|
jay-config = { version = "1.10.0", path = "jay-config" }
|
||||||
jay-toml-config = { path = "crates/toml-config" }
|
jay-toml-config = { version = "0.12.0", path = "toml-config" }
|
||||||
jay-algorithms = { path = "crates/algorithms" }
|
jay-algorithms = { version = "0.4.0", path = "algorithms" }
|
||||||
jay-geometry = { path = "crates/geometry" }
|
|
||||||
jay-layout-animation = { path = "crates/layout-animation" }
|
|
||||||
jay-formats = { path = "crates/formats" }
|
|
||||||
jay-edid = { path = "crates/edid" }
|
|
||||||
jay-units = { path = "crates/units" }
|
|
||||||
jay-utils = { path = "crates/utils" }
|
|
||||||
jay-criteria = { path = "crates/criteria" }
|
|
||||||
jay-cmm = { path = "crates/cmm" }
|
|
||||||
jay-time = { path = "crates/time" }
|
|
||||||
jay-tracy = { path = "crates/tracy" }
|
|
||||||
jay-async-engine = { path = "crates/async-engine" }
|
|
||||||
jay-io-uring = { path = "crates/io-uring" }
|
|
||||||
jay-bufio = { path = "crates/bufio" }
|
|
||||||
jay-dbus-core = { path = "crates/dbus-core" }
|
|
||||||
jay-xcon = { path = "crates/xcon" }
|
|
||||||
jay-wire-types = { path = "crates/wire-types" }
|
|
||||||
jay-wire-buf = { path = "crates/wire-buf" }
|
|
||||||
jay-tree-types = { path = "crates/tree-types" }
|
|
||||||
jay-eventfd-cache = { path = "crates/eventfd-cache" }
|
|
||||||
jay-wheel = { path = "crates/wheel" }
|
|
||||||
jay-cpu-worker = { path = "crates/cpu-worker" }
|
|
||||||
jay-sighand = { path = "crates/sighand" }
|
|
||||||
jay-pr-caps = { path = "crates/pr-caps" }
|
|
||||||
jay-bugs = { path = "crates/bugs" }
|
|
||||||
jay-logger = { path = "crates/logger" }
|
|
||||||
jay-video-types = { path = "crates/video-types" }
|
|
||||||
jay-output-types = { path = "crates/output-types" }
|
|
||||||
jay-input-types = { path = "crates/input-types" }
|
|
||||||
jay-keyboard = { path = "crates/keyboard" }
|
|
||||||
jay-gfx-types = { path = "crates/gfx-types" }
|
|
||||||
jay-theme = { path = "crates/theme" }
|
|
||||||
jay-clientmem = { path = "crates/clientmem" }
|
|
||||||
jay-allocator = { path = "crates/allocator" }
|
|
||||||
jay-output-schedule = { path = "crates/output-schedule" }
|
|
||||||
jay-drm-feedback = { path = "crates/drm-feedback" }
|
|
||||||
jay-udmabuf = { path = "crates/udmabuf" }
|
|
||||||
jay-damage = { path = "crates/damage" }
|
|
||||||
jay-pango = { path = "crates/pango" }
|
|
||||||
jay-libinput = { path = "crates/libinput" }
|
|
||||||
|
|
||||||
uapi = "0.2.13"
|
uapi = "0.2.13"
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
|
|
@ -151,6 +58,8 @@ 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"
|
||||||
|
|
@ -181,5 +90,5 @@ opt-level = 3
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
rc_tracking = []
|
rc_tracking = []
|
||||||
it = ["jay-async-engine/it", "jay-cpu-worker/it"]
|
it = []
|
||||||
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy", "jay-clientmem/tracy"]
|
tracy = ["dep:tracy-client-sys", "dep:rustc-demangle"]
|
||||||
|
|
|
||||||
|
|
@ -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, and more.
|
Vulkan and OpenGL rendering, multi-GPU support, screen sharing, and more.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "jay-algorithms"
|
name = "jay-algorithms"
|
||||||
version.workspace = true
|
version = "0.4.0"
|
||||||
edition.workspace = true
|
edition = "2024"
|
||||||
license.workspace = true
|
license = "GPL-3.0-only"
|
||||||
description = "Internal dependency of the Jay compositor"
|
description = "Internal dependency of the Jay compositor"
|
||||||
repository = "https://github.com/mahkoh/jay"
|
repository = "https://github.com/mahkoh/jay"
|
||||||
|
|
||||||
|
|
@ -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 |
|
||||||
|------|-------------------|
|
|------|-------------------|
|
||||||
| `crates/toml-spec/spec/spec.yaml` | **Canonical** TOML config spec: every key, action, match criterion, type |
|
| `toml-spec/spec/spec.yaml` | **Canonical** TOML config spec: every key, action, match criterion, type |
|
||||||
| `crates/toml-config/src/default-config.toml` | Built-in default config (keybindings, startup actions) |
|
| `toml-config/src/default-config.toml` | Built-in default config (keybindings, startup actions) |
|
||||||
| `crates/toml-config/src/config/parsers/action.rs` | Action parser — see which `type` strings are accepted |
|
| `toml-config/src/config/parsers/action.rs` | Action parser — see which `type` strings are accepted |
|
||||||
| `crates/toml-config/src/lib.rs` | Action dispatch — `window_or_seat!` macro shows which actions work in window rules |
|
| `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) |
|
||||||
| `crates/toml-config/src/config/parsers/exec.rs` | Exec parser (string, array, or table forms) |
|
| `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/crates/toml-spec/spec/spec.generated.md)
|
[spec.generated.md](https://github.com/mahkoh/jay/blob/master/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:
|
||||||
- `crates/toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
|
- `toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
|
||||||
- `crates/toml-config/src/config/parsers/action.rs` — parser (field names, types, defaults)
|
- `toml-config/src/config/parsers/action.rs` — parser (field names, types, defaults)
|
||||||
- `crates/toml-config/src/lib.rs` — dispatch (check if `window_or_seat!` is used)
|
- `toml-config/src/lib.rs` — dispatch (check if `window_or_seat!` is used)
|
||||||
- `crates/jay-config/src/input.rs` and/or `crates/jay-config/src/window.rs` — Rust API
|
- `jay-config/src/input.rs` and/or `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 `crates/toml-spec/spec/spec.yaml` for the field definition.
|
1. Read `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,6 +35,7 @@
|
||||||
- [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,6 +674,18 @@ 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,
|
||||||
|
|
@ -685,6 +697,24 @@ 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,10 +62,16 @@ 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
|
||||||
|
|
@ -74,6 +80,7 @@ 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"] },
|
||||||
|
|
@ -93,6 +100,7 @@ on-idle = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
|
privileged = true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,9 @@ 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. Setting `enable-socket` exposes an unauthenticated socket
|
emulate input events. By default, applications can only access libei through
|
||||||
that any application can use.
|
the portal (which prompts the user for permission). Setting `enable-socket`
|
||||||
|
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,6 +145,7 @@ 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.
|
||||||
|
|
@ -308,6 +309,7 @@ alt-s = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
shell = "grim - | wl-copy",
|
shell = "grim - | wl-copy",
|
||||||
|
privileged = true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -326,6 +328,12 @@ 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`:
|
||||||
|
|
@ -354,6 +362,7 @@ Print = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
shell = "grim - | wl-copy",
|
shell = "grim - | wl-copy",
|
||||||
|
privileged = true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,15 @@ 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)):
|
||||||
|
|
@ -78,6 +83,7 @@ on-idle = {
|
||||||
type = "exec",
|
type = "exec",
|
||||||
exec = {
|
exec = {
|
||||||
prog = "swaylock",
|
prog = "swaylock",
|
||||||
|
privileged = true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -91,6 +97,7 @@ 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 captured.
|
when a window is being recorded (e.g. via screen sharing).
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,5 +87,5 @@ Xwayland client itself:
|
||||||
```toml
|
```toml
|
||||||
[[clients]]
|
[[clients]]
|
||||||
match.is-xwayland = true
|
match.is-xwayland = true
|
||||||
# ... configure client-specific behavior
|
# ... grant capabilities, etc.
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,10 @@ 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
|
||||||
|
|
@ -98,6 +101,17 @@ 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.
|
||||||
|
|
@ -140,6 +154,20 @@ 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
|
||||||
|
|
@ -224,6 +252,7 @@ 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,6 +63,7 @@ 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
|
||||||
|
|
@ -128,7 +129,14 @@ 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
|
### SCHED_RR and config.so
|
||||||
|
|
||||||
|
`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`:
|
||||||
|
|
||||||
|
|
@ -136,7 +144,11 @@ 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.
|
This still allows elevated Vulkan queues and does not affect `config.so`
|
||||||
|
loading.
|
||||||
|
|
||||||
|
The mutual exclusion can be overridden at compile time by building Jay with
|
||||||
|
`JAY_ALLOW_REALTIME_CONFIG_SO=1`.
|
||||||
|
|
||||||
## Recommended Applications
|
## Recommended Applications
|
||||||
|
|
||||||
|
|
@ -144,6 +156,7 @@ 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,11 +22,12 @@
|
||||||
|
|
||||||
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, and HDR. X11 applications
|
variable refresh rate (VRR), tearing presentation, HDR, and screen sharing via
|
||||||
are supported through Xwayland.
|
xdg-desktop-portal. X11 applications are supported through Xwayland.
|
||||||
|
|
||||||
Jay is configured through a declarative TOML file. A comprehensive command-line
|
Jay is configured through a declarative TOML file, with an optional advanced
|
||||||
interface makes scripting and automation straightforward.
|
mode that uses a shared library for programmatic control. A comprehensive
|
||||||
|
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 ask you to select a window, indicated by a
|
**Toplevel selection.** Some actions (like screen sharing) ask you to select a
|
||||||
purple overlay. During this selection, right-click a
|
window, indicated by 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
|
||||||
|
|
|
||||||
111
book/src/screen-sharing.md
Normal file
111
book/src/screen-sharing.md
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
# 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`
|
||||||
|
|
@ -77,20 +77,6 @@ You can also right-click any title in a container to toggle mono mode.
|
||||||
In mono mode, scroll over the title bar to cycle between windows in the
|
In mono mode, scroll over the title bar to cycle between windows in the
|
||||||
container.
|
container.
|
||||||
|
|
||||||
## Autotiling
|
|
||||||
|
|
||||||
Autotiling makes newly tiled windows alternate split direction from the focused
|
|
||||||
tiled window. The first split uses the containing group direction, then later
|
|
||||||
windows wrap the focused tile in the opposite direction, producing a horizontal,
|
|
||||||
vertical, horizontal pattern as the layout grows.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[shortcuts]
|
|
||||||
alt-a = "toggle-autotile"
|
|
||||||
```
|
|
||||||
|
|
||||||
Manual grouping and split commands still use the direction you request.
|
|
||||||
|
|
||||||
## Fullscreen
|
## Fullscreen
|
||||||
|
|
||||||
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
|
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,57 @@ 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:
|
||||||
|
|
@ -81,6 +132,45 @@ 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,6 +31,12 @@ 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
|
||||||
|
|
@ -64,6 +70,142 @@ 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]]`
|
||||||
|
|
@ -314,6 +456,18 @@ 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,15 +123,16 @@ laptop.
|
||||||
|
|
||||||
## Workspace Capture
|
## Workspace Capture
|
||||||
|
|
||||||
By default, newly created workspaces can be captured by capture clients. You can
|
By default, newly created workspaces can be captured for screen sharing. You
|
||||||
disable this globally:
|
can disable this globally:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
workspace-capture = false
|
workspace-capture = false
|
||||||
```
|
```
|
||||||
|
|
||||||
When workspace capture is enabled, compositor-native capture clients may capture
|
When workspace capture is enabled, screen-sharing applications can share
|
||||||
individual workspaces instead of whole outputs.
|
individual workspaces (in addition to full outputs and individual windows). See
|
||||||
|
[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,27 +4,18 @@ use {
|
||||||
std::{env, io::Write},
|
std::{env, io::Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
#[expect(unused_macros)]
|
||||||
macro_rules! cenum {
|
#[macro_use]
|
||||||
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
|
#[path = "../src/macros.rs"]
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
mod macros;
|
||||||
pub struct $name(pub i32);
|
|
||||||
|
|
||||||
impl $name {
|
#[path = "../src/libinput/consts.rs"]
|
||||||
pub fn raw(self) -> i32 {
|
mod libinput;
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const $uc: &[i32] = &[$($val,)*];
|
#[path = "../src/pango/consts.rs"]
|
||||||
|
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 {
|
||||||
|
|
@ -58,6 +49,108 @@ 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")?;
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,17 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() -> anyhow::Result<()> {
|
pub fn main() -> anyhow::Result<()> {
|
||||||
|
create_bridge()?;
|
||||||
create_version()?;
|
create_version()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_bridge() -> anyhow::Result<()> {
|
||||||
|
println!("cargo:rerun-if-changed=src/bridge.c");
|
||||||
|
cc::Build::new().file("src/bridge.c").compile("bridge");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn create_version() -> anyhow::Result<()> {
|
fn create_version() -> anyhow::Result<()> {
|
||||||
let mut version_string = env!("CARGO_PKG_VERSION").to_string();
|
let mut version_string = env!("CARGO_PKG_VERSION").to_string();
|
||||||
if let Ok(output) = Command::new("git").arg("rev-parse").arg("HEAD").output()
|
if let Ok(output) = Command::new("git").arg("rev-parse").arg("HEAD").output()
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,6 @@ 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)?;
|
||||||
|
|
@ -443,7 +442,7 @@ pub fn main() -> Result<()> {
|
||||||
writeln!(f, "use std::rc::Rc;")?;
|
writeln!(f, "use std::rc::Rc;")?;
|
||||||
writeln!(f, "use uapi::OwnedFd;")?;
|
writeln!(f, "use uapi::OwnedFd;")?;
|
||||||
writeln!(f, "use bstr::BStr;")?;
|
writeln!(f, "use bstr::BStr;")?;
|
||||||
writeln!(f, "use jay_units::fixed::Fixed;")?;
|
writeln!(f, "use crate::fixed::Fixed;")?;
|
||||||
writeln!(f, "use crate::client::{{EventFormatter, RequestParser}};")?;
|
writeln!(f, "use crate::client::{{EventFormatter, RequestParser}};")?;
|
||||||
writeln!(f, "use crate::object::{{ObjectId, Interface}};")?;
|
writeln!(f, "use crate::object::{{ObjectId, Interface}};")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-allocator"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-formats = { path = "../formats" }
|
|
||||||
jay-video-types = { path = "../video-types" }
|
|
||||||
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-async-engine"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-time = { path = "../time" }
|
|
||||||
jay-tracy = { path = "../tracy" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
it = []
|
|
||||||
tracy = ["jay-tracy/tracy"]
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-bufio"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-io-uring = { path = "../io-uring" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-bugs"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
ahash = "0.8.7"
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-clientmem"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-cpu-worker = { path = "../cpu-worker" }
|
|
||||||
jay-gfx-types = { path = "../gfx-types" }
|
|
||||||
jay-tracy = { path = "../tracy" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
log = "0.4.20"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
tracy = ["jay-tracy/tracy"]
|
|
||||||
|
|
@ -1,331 +0,0 @@
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-cmm"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-cpu-worker"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-async-engine = { path = "../async-engine" }
|
|
||||||
jay-geometry = { path = "../geometry" }
|
|
||||||
jay-io-uring = { path = "../io-uring" }
|
|
||||||
jay-tracy = { path = "../tracy" }
|
|
||||||
jay-utils = { 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 = { path = "../wheel" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
it = []
|
|
||||||
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy"]
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-criteria"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
ahash = "0.8.7"
|
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
|
||||||
regex = "1.11.1"
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
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 {}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-damage"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-geometry = { path = "../geometry" }
|
|
||||||
jay-tree-types = { path = "../tree-types" }
|
|
||||||
jay-units = { path = "../units" }
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
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 _,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-dbus-core"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-bufio = { path = "../bufio" }
|
|
||||||
jay-io-uring = { path = "../io-uring" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,232 +0,0 @@
|
||||||
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,13 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-drm-feedback"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-video-types = { path = "../video-types" }
|
|
||||||
|
|
||||||
ahash = "0.8.7"
|
|
||||||
byteorder = "1.5.0"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-edid"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
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"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-eventfd-cache"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-async-engine = { path = "../async-engine" }
|
|
||||||
jay-io-uring = { path = "../io-uring" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
log = { version = "0.4.20", features = ["std"] }
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-formats"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
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 = { path = "../jay-config" }
|
|
||||||
|
|
@ -1,559 +0,0 @@
|
||||||
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,
|
|
||||||
];
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-geometry"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Geometry primitives for Jay"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-algorithms = { path = "../algorithms" }
|
|
||||||
smallvec = { version = "1.11.1", features = ["const_generics", "const_new", "union"] }
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-gfx-types"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-input-types"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Input data types for the Jay compositor"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-output-types = { path = "../output-types" }
|
|
||||||
jay-units = { path = "../units" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
|
||||||
|
|
@ -1,482 +0,0 @@
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-io-uring"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-async-engine = { path = "../async-engine" }
|
|
||||||
jay-time = { path = "../time" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
log = { version = "0.4.20", features = ["std"] }
|
|
||||||
run-on-drop = "1.0.0"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-config-schema"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Shared configuration schema declarations for the Jay compositor"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
ahash = "0.8.11"
|
|
||||||
jay-config = { path = "../jay-config" }
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
use jay_config::{
|
|
||||||
Direction,
|
|
||||||
input::{LayerDirection, Timeline},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum SimpleCommand {
|
|
||||||
Close,
|
|
||||||
DisablePointerConstraint,
|
|
||||||
Focus(Direction),
|
|
||||||
FocusParent,
|
|
||||||
Move(Direction),
|
|
||||||
None,
|
|
||||||
Quit,
|
|
||||||
ReloadConfigToml,
|
|
||||||
ToggleFloating,
|
|
||||||
SetFloating(bool),
|
|
||||||
ToggleFullscreen,
|
|
||||||
SetFullscreen(bool),
|
|
||||||
SendToScratchpad,
|
|
||||||
ToggleScratchpad,
|
|
||||||
CycleScratchpad,
|
|
||||||
Forward(bool),
|
|
||||||
EnableWindowManagement(bool),
|
|
||||||
SetFloatAboveFullscreen(bool),
|
|
||||||
ToggleFloatAboveFullscreen,
|
|
||||||
SetFloatPinned(bool),
|
|
||||||
ToggleFloatPinned,
|
|
||||||
KillClient,
|
|
||||||
ShowBar(bool),
|
|
||||||
ToggleBar,
|
|
||||||
ShowTitles(bool),
|
|
||||||
ToggleTitles,
|
|
||||||
FloatTitles(bool),
|
|
||||||
ToggleFloatTitles,
|
|
||||||
FocusHistory(Timeline),
|
|
||||||
FocusLayerRel(LayerDirection),
|
|
||||||
FocusTiles,
|
|
||||||
ToggleFocusFloatTiled,
|
|
||||||
CreateMark,
|
|
||||||
JumpToMark,
|
|
||||||
PopMode(bool),
|
|
||||||
EnableSimpleIm(bool),
|
|
||||||
ToggleSimpleImEnabled,
|
|
||||||
ReloadSimpleIm,
|
|
||||||
EnableUnicodeInput,
|
|
||||||
WarpMouseToFocus,
|
|
||||||
ToggleTab,
|
|
||||||
MakeGroupH,
|
|
||||||
MakeGroupV,
|
|
||||||
MakeGroupTab,
|
|
||||||
ChangeGroupOpposite,
|
|
||||||
Equalize,
|
|
||||||
EqualizeRecursive,
|
|
||||||
MoveTabLeft,
|
|
||||||
MoveTabRight,
|
|
||||||
SetAutotile(bool),
|
|
||||||
ToggleAutotile,
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Animations {
|
|
||||||
pub enabled: Option<bool>,
|
|
||||||
pub duration_ms: Option<u32>,
|
|
||||||
pub style: Option<String>,
|
|
||||||
pub curve: Option<AnimationCurveConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum AnimationCurveConfig {
|
|
||||||
Preset(String),
|
|
||||||
CubicBezier([f32; 4]),
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
use jay_config::status::MessageFormat;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Exec {
|
|
||||||
pub prog: String,
|
|
||||||
pub args: Vec<String>,
|
|
||||||
pub envs: Vec<(String, String)>,
|
|
||||||
pub tag: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Status {
|
|
||||||
pub format: MessageFormat,
|
|
||||||
pub exec: Exec,
|
|
||||||
pub separator: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum InputMatch {
|
|
||||||
Any(Vec<InputMatch>),
|
|
||||||
All {
|
|
||||||
tag: Option<String>,
|
|
||||||
name: Option<String>,
|
|
||||||
syspath: Option<String>,
|
|
||||||
devnode: Option<String>,
|
|
||||||
is_keyboard: Option<bool>,
|
|
||||||
is_pointer: Option<bool>,
|
|
||||||
is_touch: Option<bool>,
|
|
||||||
is_tablet_tool: Option<bool>,
|
|
||||||
is_tablet_pad: Option<bool>,
|
|
||||||
is_gesture: Option<bool>,
|
|
||||||
is_switch: Option<bool>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
use jay_config::keyboard::Keymap;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ConfigKeymap {
|
|
||||||
Named(String),
|
|
||||||
Literal(Keymap),
|
|
||||||
Defined { name: String, map: Keymap },
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
//! Shared configuration schema declarations for Jay.
|
|
||||||
//!
|
|
||||||
//! This crate is the target home for option structs, defaults, validation
|
|
||||||
//! policy, and docs metadata that need to be consumed by TOML parsing,
|
|
||||||
//! generated config documentation, and compositor-side application code.
|
|
||||||
|
|
||||||
pub mod action;
|
|
||||||
pub mod animations;
|
|
||||||
pub mod command;
|
|
||||||
pub mod input;
|
|
||||||
pub mod keymap;
|
|
||||||
pub mod model;
|
|
||||||
pub mod options;
|
|
||||||
pub mod output;
|
|
||||||
pub mod rules;
|
|
||||||
pub mod theme;
|
|
||||||
|
|
||||||
pub use action::SimpleCommand;
|
|
||||||
pub use animations::{AnimationCurveConfig, Animations};
|
|
||||||
pub use command::{Exec, Status};
|
|
||||||
pub use input::InputMatch;
|
|
||||||
pub use keymap::ConfigKeymap;
|
|
||||||
pub use model::{
|
|
||||||
Action, ClientRule, Config, Input, InputMode, NamedAction, Scratchpad, Shortcut, WindowRule,
|
|
||||||
};
|
|
||||||
pub use options::{
|
|
||||||
ColorManagement, Float, FocusHistory, Libei, RepeatRate, SimpleIm, Tearing, UiDrag, Vrr,
|
|
||||||
Xwayland,
|
|
||||||
};
|
|
||||||
pub use output::{
|
|
||||||
ConfigConnector, ConfigDrmDevice, ConnectorMatch, DrmDeviceMatch, Mode, Output, OutputMatch,
|
|
||||||
};
|
|
||||||
pub use rules::{ClientMatch, GenericMatch, MatchExactly, WindowMatch};
|
|
||||||
pub use theme::Theme;
|
|
||||||
|
|
@ -1,256 +0,0 @@
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
Animations, ClientMatch, ColorManagement, ConfigConnector, ConfigDrmDevice, ConfigKeymap,
|
|
||||||
DrmDeviceMatch, Exec, Float, FocusHistory, InputMatch, Libei, Output, OutputMatch,
|
|
||||||
RepeatRate, SimpleCommand, SimpleIm, Status, Tearing, Theme, UiDrag, Vrr, WindowMatch,
|
|
||||||
Xwayland,
|
|
||||||
},
|
|
||||||
ahash::AHashMap,
|
|
||||||
jay_config::{
|
|
||||||
Direction, Workspace,
|
|
||||||
input::{
|
|
||||||
FallbackOutputMode, SwitchEvent, acceleration::AccelProfile, clickmethod::ClickMethod,
|
|
||||||
},
|
|
||||||
keyboard::{ModifiedKeySym, mods::Modifiers, syms::KeySym},
|
|
||||||
logging::LogLevel,
|
|
||||||
video::GfxApi,
|
|
||||||
window::TileState,
|
|
||||||
workspace::WorkspaceDisplayOrder,
|
|
||||||
},
|
|
||||||
std::{rc::Rc, time::Duration},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[expect(clippy::enum_variant_names)]
|
|
||||||
pub enum Action {
|
|
||||||
ConfigureConnector {
|
|
||||||
con: ConfigConnector,
|
|
||||||
},
|
|
||||||
ConfigureDirectScanout {
|
|
||||||
enabled: bool,
|
|
||||||
},
|
|
||||||
ConfigureDrmDevice {
|
|
||||||
dev: ConfigDrmDevice,
|
|
||||||
},
|
|
||||||
ConfigureIdle {
|
|
||||||
idle: Option<Duration>,
|
|
||||||
grace_period: Option<Duration>,
|
|
||||||
},
|
|
||||||
ConfigureInput {
|
|
||||||
input: Box<Input>,
|
|
||||||
},
|
|
||||||
ConfigureOutput {
|
|
||||||
out: Output,
|
|
||||||
},
|
|
||||||
Exec {
|
|
||||||
exec: Exec,
|
|
||||||
},
|
|
||||||
MoveToWorkspace {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
SendToScratchpad {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
ToggleScratchpad {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
CycleScratchpad {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
Multi {
|
|
||||||
actions: Vec<Action>,
|
|
||||||
},
|
|
||||||
SetEnv {
|
|
||||||
env: Vec<(String, String)>,
|
|
||||||
},
|
|
||||||
SetGfxApi {
|
|
||||||
api: GfxApi,
|
|
||||||
},
|
|
||||||
SetKeymap {
|
|
||||||
map: ConfigKeymap,
|
|
||||||
},
|
|
||||||
SetLogLevel {
|
|
||||||
level: LogLevel,
|
|
||||||
},
|
|
||||||
SetRenderDevice {
|
|
||||||
dev: Box<DrmDeviceMatch>,
|
|
||||||
},
|
|
||||||
SetStatus {
|
|
||||||
status: Option<Status>,
|
|
||||||
},
|
|
||||||
SetTheme {
|
|
||||||
theme: Box<Theme>,
|
|
||||||
},
|
|
||||||
ShowWorkspace {
|
|
||||||
name: String,
|
|
||||||
output: Option<OutputMatch>,
|
|
||||||
},
|
|
||||||
SimpleCommand {
|
|
||||||
cmd: SimpleCommand,
|
|
||||||
},
|
|
||||||
SwitchToVt {
|
|
||||||
num: u32,
|
|
||||||
},
|
|
||||||
UnsetEnv {
|
|
||||||
env: Vec<String>,
|
|
||||||
},
|
|
||||||
MoveToOutput {
|
|
||||||
workspace: Option<Workspace>,
|
|
||||||
output: Option<OutputMatch>,
|
|
||||||
direction: Option<Direction>,
|
|
||||||
},
|
|
||||||
SetRepeatRate {
|
|
||||||
rate: RepeatRate,
|
|
||||||
},
|
|
||||||
DefineAction {
|
|
||||||
name: String,
|
|
||||||
action: Box<Action>,
|
|
||||||
},
|
|
||||||
UndefineAction {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
NamedAction {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
CreateMark(u32),
|
|
||||||
JumpToMark(u32),
|
|
||||||
CopyMark(u32, u32),
|
|
||||||
SetMode {
|
|
||||||
name: String,
|
|
||||||
latch: bool,
|
|
||||||
},
|
|
||||||
CreateVirtualOutput {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
RemoveVirtualOutput {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
Resize {
|
|
||||||
dx1: i32,
|
|
||||||
dy1: i32,
|
|
||||||
dx2: i32,
|
|
||||||
dy2: i32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ClientRule {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub match_: ClientMatch,
|
|
||||||
pub action: Option<Action>,
|
|
||||||
pub latch: Option<Action>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct WindowRule {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub match_: WindowMatch,
|
|
||||||
pub action: Option<Action>,
|
|
||||||
pub latch: Option<Action>,
|
|
||||||
pub auto_focus: Option<bool>,
|
|
||||||
pub initial_tile_state: Option<TileState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Input {
|
|
||||||
pub tag: Option<String>,
|
|
||||||
pub match_: InputMatch,
|
|
||||||
pub accel_profile: Option<AccelProfile>,
|
|
||||||
pub accel_speed: Option<f64>,
|
|
||||||
pub tap_enabled: Option<bool>,
|
|
||||||
pub tap_drag_enabled: Option<bool>,
|
|
||||||
pub tap_drag_lock_enabled: Option<bool>,
|
|
||||||
pub left_handed: Option<bool>,
|
|
||||||
pub natural_scrolling: Option<bool>,
|
|
||||||
pub click_method: Option<ClickMethod>,
|
|
||||||
pub middle_button_emulation: Option<bool>,
|
|
||||||
pub px_per_wheel_scroll: Option<f64>,
|
|
||||||
pub transform_matrix: Option<[[f64; 2]; 2]>,
|
|
||||||
pub keymap: Option<ConfigKeymap>,
|
|
||||||
pub switch_actions: AHashMap<SwitchEvent, Action>,
|
|
||||||
pub output: Option<Option<OutputMatch>>,
|
|
||||||
pub calibration_matrix: Option<[[f32; 3]; 2]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Shortcut {
|
|
||||||
pub mask: Modifiers,
|
|
||||||
pub keysym: ModifiedKeySym,
|
|
||||||
pub action: Action,
|
|
||||||
pub latch: Option<Action>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct NamedAction {
|
|
||||||
pub name: Rc<String>,
|
|
||||||
pub action: Action,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct InputMode {
|
|
||||||
pub parent: Option<String>,
|
|
||||||
pub shortcuts: Vec<Shortcut>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Config {
|
|
||||||
pub keymap: Option<ConfigKeymap>,
|
|
||||||
pub repeat_rate: Option<RepeatRate>,
|
|
||||||
pub shortcuts: Vec<Shortcut>,
|
|
||||||
pub on_graphics_initialized: Option<Action>,
|
|
||||||
pub on_idle: Option<Action>,
|
|
||||||
pub status: Option<Status>,
|
|
||||||
pub connectors: Vec<ConfigConnector>,
|
|
||||||
pub outputs: Vec<Output>,
|
|
||||||
pub workspace_capture: bool,
|
|
||||||
pub env: Vec<(String, String)>,
|
|
||||||
pub on_startup: Option<Action>,
|
|
||||||
pub keymaps: Vec<ConfigKeymap>,
|
|
||||||
pub auto_reload: Option<bool>,
|
|
||||||
pub log_level: Option<LogLevel>,
|
|
||||||
pub clean_logs_older_than: Option<Duration>,
|
|
||||||
pub theme: Theme,
|
|
||||||
pub gfx_api: Option<GfxApi>,
|
|
||||||
pub direct_scanout_enabled: Option<bool>,
|
|
||||||
pub drm_devices: Vec<ConfigDrmDevice>,
|
|
||||||
pub render_device: Option<DrmDeviceMatch>,
|
|
||||||
pub inputs: Vec<Input>,
|
|
||||||
pub idle: Option<Duration>,
|
|
||||||
pub grace_period: Option<Duration>,
|
|
||||||
pub key_press_enables_dpms: Option<bool>,
|
|
||||||
pub mouse_move_enables_dpms: Option<bool>,
|
|
||||||
pub explicit_sync_enabled: Option<bool>,
|
|
||||||
pub focus_follows_mouse: bool,
|
|
||||||
pub window_management_key: Option<ModifiedKeySym>,
|
|
||||||
pub vrr: Option<Vrr>,
|
|
||||||
pub tearing: Option<Tearing>,
|
|
||||||
pub libei: Libei,
|
|
||||||
pub ui_drag: UiDrag,
|
|
||||||
pub animations: Animations,
|
|
||||||
pub xwayland: Option<Xwayland>,
|
|
||||||
pub color_management: Option<ColorManagement>,
|
|
||||||
pub float: Option<Float>,
|
|
||||||
pub named_actions: Vec<NamedAction>,
|
|
||||||
pub max_action_depth: u64,
|
|
||||||
pub client_rules: Vec<ClientRule>,
|
|
||||||
pub window_rules: Vec<WindowRule>,
|
|
||||||
pub pointer_revert_key: Option<KeySym>,
|
|
||||||
pub use_hardware_cursor: Option<bool>,
|
|
||||||
pub show_bar: Option<bool>,
|
|
||||||
pub show_titles: Option<bool>,
|
|
||||||
pub focus_history: Option<FocusHistory>,
|
|
||||||
pub middle_click_paste: Option<bool>,
|
|
||||||
pub input_modes: AHashMap<String, InputMode>,
|
|
||||||
pub workspace_display_order: Option<WorkspaceDisplayOrder>,
|
|
||||||
pub simple_im: Option<SimpleIm>,
|
|
||||||
pub fallback_output_mode: Option<FallbackOutputMode>,
|
|
||||||
pub mouse_follows_focus: Option<bool>,
|
|
||||||
pub scratchpads: Vec<Scratchpad>,
|
|
||||||
pub autotile: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Scratchpad {
|
|
||||||
pub name: String,
|
|
||||||
pub exec: Option<Exec>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
use jay_config::{
|
|
||||||
video::{TearingMode, VrrMode},
|
|
||||||
xwayland::XScalingMode,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct UiDrag {
|
|
||||||
pub enabled: Option<bool>,
|
|
||||||
pub threshold: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ColorManagement {
|
|
||||||
pub enabled: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Float {
|
|
||||||
pub show_pin_icon: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FocusHistory {
|
|
||||||
pub only_visible: Option<bool>,
|
|
||||||
pub same_workspace: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RepeatRate {
|
|
||||||
pub rate: i32,
|
|
||||||
pub delay: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Vrr {
|
|
||||||
pub mode: Option<VrrMode>,
|
|
||||||
pub cursor_hz: Option<f64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SimpleIm {
|
|
||||||
pub enabled: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Xwayland {
|
|
||||||
pub enabled: Option<bool>,
|
|
||||||
pub scaling_mode: Option<XScalingMode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Tearing {
|
|
||||||
pub mode: Option<TearingMode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Libei {
|
|
||||||
pub enable_socket: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
use {
|
|
||||||
crate::{Tearing, Vrr},
|
|
||||||
jay_config::video::{BlendSpace, ColorSpace, Eotf, Format, GfxApi, Transform},
|
|
||||||
std::fmt::{Display, Formatter},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum OutputMatch {
|
|
||||||
Any(Vec<OutputMatch>),
|
|
||||||
All {
|
|
||||||
name: Option<String>,
|
|
||||||
connector: Option<String>,
|
|
||||||
serial_number: Option<String>,
|
|
||||||
manufacturer: Option<String>,
|
|
||||||
model: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum DrmDeviceMatch {
|
|
||||||
Any(Vec<DrmDeviceMatch>),
|
|
||||||
All {
|
|
||||||
name: Option<String>,
|
|
||||||
syspath: Option<String>,
|
|
||||||
vendor: Option<u32>,
|
|
||||||
vendor_name: Option<String>,
|
|
||||||
model: Option<u32>,
|
|
||||||
model_name: Option<String>,
|
|
||||||
devnode: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Mode {
|
|
||||||
pub width: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub refresh_rate: Option<f64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Mode {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{} x {}", self.width, self.height)?;
|
|
||||||
if let Some(rr) = self.refresh_rate {
|
|
||||||
write!(f, " @ {rr}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Output {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub match_: OutputMatch,
|
|
||||||
pub x: Option<i32>,
|
|
||||||
pub y: Option<i32>,
|
|
||||||
pub scale: Option<f64>,
|
|
||||||
pub transform: Option<Transform>,
|
|
||||||
pub mode: Option<Mode>,
|
|
||||||
pub vrr: Option<Vrr>,
|
|
||||||
pub tearing: Option<Tearing>,
|
|
||||||
pub format: Option<Format>,
|
|
||||||
pub color_space: Option<ColorSpace>,
|
|
||||||
pub eotf: Option<Eotf>,
|
|
||||||
pub brightness: Option<Option<f64>>,
|
|
||||||
pub blend_space: Option<BlendSpace>,
|
|
||||||
pub use_native_gamut: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ConnectorMatch {
|
|
||||||
Any(Vec<ConnectorMatch>),
|
|
||||||
All { connector: Option<String> },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ConfigConnector {
|
|
||||||
pub match_: ConnectorMatch,
|
|
||||||
pub enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ConfigDrmDevice {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub match_: DrmDeviceMatch,
|
|
||||||
pub gfx_api: Option<GfxApi>,
|
|
||||||
pub direct_scanout_enabled: Option<bool>,
|
|
||||||
pub flip_margin_ms: Option<f64>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
use jay_config::window::{ContentType, WindowType};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
pub struct GenericMatch<Match> {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub not: Option<Box<Match>>,
|
|
||||||
pub all: Option<Vec<Match>>,
|
|
||||||
pub any: Option<Vec<Match>>,
|
|
||||||
pub exactly: Option<MatchExactly<Match>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MatchExactly<Match> {
|
|
||||||
pub num: usize,
|
|
||||||
pub list: Vec<Match>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
pub struct ClientMatch {
|
|
||||||
pub generic: GenericMatch<Self>,
|
|
||||||
pub sandbox_engine: Option<String>,
|
|
||||||
pub sandbox_engine_regex: Option<String>,
|
|
||||||
pub sandbox_app_id: Option<String>,
|
|
||||||
pub sandbox_app_id_regex: Option<String>,
|
|
||||||
pub sandbox_instance_id: Option<String>,
|
|
||||||
pub sandbox_instance_id_regex: Option<String>,
|
|
||||||
pub sandboxed: Option<bool>,
|
|
||||||
pub uid: Option<i32>,
|
|
||||||
pub pid: Option<i32>,
|
|
||||||
pub is_xwayland: Option<bool>,
|
|
||||||
pub comm: Option<String>,
|
|
||||||
pub comm_regex: Option<String>,
|
|
||||||
pub exe: Option<String>,
|
|
||||||
pub exe_regex: Option<String>,
|
|
||||||
pub tag: Option<String>,
|
|
||||||
pub tag_regex: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
pub struct WindowMatch {
|
|
||||||
pub generic: GenericMatch<Self>,
|
|
||||||
pub types: Option<WindowType>,
|
|
||||||
pub client: Option<ClientMatch>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub title_regex: Option<String>,
|
|
||||||
pub app_id: Option<String>,
|
|
||||||
pub app_id_regex: Option<String>,
|
|
||||||
pub floating: Option<bool>,
|
|
||||||
pub visible: Option<bool>,
|
|
||||||
pub urgent: Option<bool>,
|
|
||||||
pub focused: Option<bool>,
|
|
||||||
pub fullscreen: Option<bool>,
|
|
||||||
pub just_mapped: Option<bool>,
|
|
||||||
pub tag: Option<String>,
|
|
||||||
pub tag_regex: Option<String>,
|
|
||||||
pub x_class: Option<String>,
|
|
||||||
pub x_class_regex: Option<String>,
|
|
||||||
pub x_instance: Option<String>,
|
|
||||||
pub x_instance_regex: Option<String>,
|
|
||||||
pub x_role: Option<String>,
|
|
||||||
pub x_role_regex: Option<String>,
|
|
||||||
pub workspace: Option<String>,
|
|
||||||
pub workspace_regex: Option<String>,
|
|
||||||
pub content_types: Option<ContentType>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
use jay_config::theme::{BarPosition, Color};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Theme {
|
|
||||||
pub attention_requested_bg_color: Option<Color>,
|
|
||||||
pub bg_color: Option<Color>,
|
|
||||||
pub bar_bg_color: Option<Color>,
|
|
||||||
pub bar_status_text_color: Option<Color>,
|
|
||||||
pub border_color: Option<Color>,
|
|
||||||
pub active_border_color: Option<Color>,
|
|
||||||
pub captured_focused_title_bg_color: Option<Color>,
|
|
||||||
pub captured_unfocused_title_bg_color: Option<Color>,
|
|
||||||
pub focused_inactive_title_bg_color: Option<Color>,
|
|
||||||
pub focused_inactive_title_text_color: Option<Color>,
|
|
||||||
pub focused_title_bg_color: Option<Color>,
|
|
||||||
pub focused_title_text_color: Option<Color>,
|
|
||||||
pub separator_color: Option<Color>,
|
|
||||||
pub unfocused_title_bg_color: Option<Color>,
|
|
||||||
pub unfocused_title_text_color: Option<Color>,
|
|
||||||
pub highlight_color: Option<Color>,
|
|
||||||
pub border_width: Option<i32>,
|
|
||||||
pub title_height: Option<i32>,
|
|
||||||
pub bar_height: Option<i32>,
|
|
||||||
pub font: Option<String>,
|
|
||||||
pub title_font: Option<String>,
|
|
||||||
pub bar_font: Option<String>,
|
|
||||||
pub bar_position: Option<BarPosition>,
|
|
||||||
pub bar_separator_width: Option<i32>,
|
|
||||||
pub gap: Option<i32>,
|
|
||||||
pub floating_titles: Option<bool>,
|
|
||||||
pub title_gap: Option<i32>,
|
|
||||||
pub corner_radius: Option<f32>,
|
|
||||||
pub tab_active_bg_color: Option<Color>,
|
|
||||||
pub tab_active_border_color: Option<Color>,
|
|
||||||
pub tab_inactive_bg_color: Option<Color>,
|
|
||||||
pub tab_inactive_border_color: Option<Color>,
|
|
||||||
pub tab_active_text_color: Option<Color>,
|
|
||||||
pub tab_inactive_text_color: Option<Color>,
|
|
||||||
pub tab_bar_bg_color: Option<Color>,
|
|
||||||
pub tab_attention_bg_color: Option<Color>,
|
|
||||||
pub tab_bar_height: Option<i32>,
|
|
||||||
pub tab_bar_padding: Option<i32>,
|
|
||||||
pub tab_bar_radius: Option<i32>,
|
|
||||||
pub tab_bar_border_width: Option<i32>,
|
|
||||||
pub tab_bar_text_padding: Option<i32>,
|
|
||||||
pub tab_bar_gap: Option<i32>,
|
|
||||||
pub tab_title_align: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
pub mod client;
|
|
||||||
mod logging;
|
|
||||||
|
|
||||||
pub use crate::protocol::{
|
|
||||||
ClientCriterionPayload, ClientCriterionStringField, ConfigEntry, ConfigHandler,
|
|
||||||
DEFAULT_SEAT_NAME, GenericCriterionPayload, PollableId, ServerHandler, Unref, VERSION,
|
|
||||||
WindowCriterionPayload, WindowCriterionStringField, WireMode,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod messages {
|
|
||||||
pub use crate::protocol::{
|
|
||||||
ClientMessage, InitMessage, Response, ServerFeature, ServerMessage, V1InitMessage,
|
|
||||||
WorkspaceSource,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-keyboard"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Keyboard state and keymap helpers for the Jay compositor"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-input-types = { path = "../input-types" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
blake3 = "1.8.2"
|
|
||||||
kbvm = { version = "0.1.6", features = ["compose"] }
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,357 +0,0 @@
|
||||||
use {
|
|
||||||
jay_input_types::{
|
|
||||||
LED_CAPS_LOCK, LED_COMPOSE, LED_KANA, LED_NUM_LOCK, LED_SCROLL_LOCK, Leds,
|
|
||||||
},
|
|
||||||
jay_utils::{
|
|
||||||
event_listener::EventSource,
|
|
||||||
numcell::NumCell,
|
|
||||||
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
|
||||||
syncqueue::SyncQueue,
|
|
||||||
vecset::VecSet,
|
|
||||||
},
|
|
||||||
kbvm::{
|
|
||||||
Components,
|
|
||||||
lookup::LookupTable,
|
|
||||||
state_machine::{self, Event, StateMachine},
|
|
||||||
xkb::{
|
|
||||||
self, Keymap,
|
|
||||||
diagnostic::{Diagnostic, WriteToLog},
|
|
||||||
keymap::{Indicator, IndicatorMatcher},
|
|
||||||
rmlvo::Group,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
std::{
|
|
||||||
cell::{Ref, RefCell},
|
|
||||||
io::Write,
|
|
||||||
rc::Rc,
|
|
||||||
},
|
|
||||||
thiserror::Error,
|
|
||||||
uapi::{OwnedFd, c},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum KeyboardError {
|
|
||||||
#[error("Could not create a keymap memfd")]
|
|
||||||
KeymapMemfd(#[source] OsError),
|
|
||||||
#[error("Could not copy the keymap")]
|
|
||||||
KeymapCopy(#[source] OsError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct KeyboardStateIds {
|
|
||||||
next: NumCell<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for KeyboardStateIds {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
next: NumCell::new(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyboardStateIds {
|
|
||||||
pub fn next(&self) -> KeyboardStateId {
|
|
||||||
KeyboardStateId(self.next.fetch_add(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct KeyboardStateId(u64);
|
|
||||||
|
|
||||||
impl KeyboardStateId {
|
|
||||||
pub fn raw(&self) -> u64 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_raw(id: u64) -> Self {
|
|
||||||
Self(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for KeyboardStateId {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
std::fmt::Display::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum KbvmError {
|
|
||||||
#[error("could not parse the keymap")]
|
|
||||||
CouldNotParseKeymap(#[source] Diagnostic),
|
|
||||||
#[error("Could not create a keymap memfd")]
|
|
||||||
KeymapMemfd(#[source] OsError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KbvmContext {
|
|
||||||
pub ctx: xkb::Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for KbvmContext {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut ctx = xkb::Context::builder();
|
|
||||||
ctx.enable_environment(true);
|
|
||||||
Self { ctx: ctx.build() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
|
||||||
pub struct KbvmMapId([u8; 32]);
|
|
||||||
|
|
||||||
pub struct KbvmMap {
|
|
||||||
pub id: KbvmMapId,
|
|
||||||
pub state_machine: StateMachine,
|
|
||||||
pub lookup_table: LookupTable,
|
|
||||||
pub map: KeymapFd,
|
|
||||||
pub xwayland_map: KeymapFd,
|
|
||||||
pub has_indicators: bool,
|
|
||||||
pub num_lock: Option<IndicatorMatcher>,
|
|
||||||
pub caps_lock: Option<IndicatorMatcher>,
|
|
||||||
pub scroll_lock: Option<IndicatorMatcher>,
|
|
||||||
pub compose: Option<IndicatorMatcher>,
|
|
||||||
pub kana: Option<IndicatorMatcher>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KbvmState {
|
|
||||||
pub map: Rc<KbvmMap>,
|
|
||||||
pub state: state_machine::State,
|
|
||||||
pub kb_state: KeyboardState,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KeyboardState {
|
|
||||||
pub id: KeyboardStateId,
|
|
||||||
pub map: Rc<KbvmMap>,
|
|
||||||
pub pressed_keys: VecSet<u32>,
|
|
||||||
pub mods: Components,
|
|
||||||
pub leds: Leds,
|
|
||||||
pub leds_changed: EventSource<dyn LedsListener>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait LedsListener {
|
|
||||||
fn leds(&self, leds: Leds);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DynKeyboardState {
|
|
||||||
fn borrow(&self) -> Ref<'_, KeyboardState>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynKeyboardState for RefCell<KeyboardState> {
|
|
||||||
fn borrow(&self) -> Ref<'_, KeyboardState> {
|
|
||||||
self.borrow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynKeyboardState for RefCell<KbvmState> {
|
|
||||||
fn borrow(&self) -> Ref<'_, KeyboardState> {
|
|
||||||
Ref::map(self.borrow(), |v| &v.kb_state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyboardState {
|
|
||||||
pub fn apply_event(&mut self, event: Event) -> bool {
|
|
||||||
let changed = self.mods.apply_event(event);
|
|
||||||
if changed && self.map.has_indicators {
|
|
||||||
self.update_leds();
|
|
||||||
}
|
|
||||||
changed
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_leds(&mut self) {
|
|
||||||
if !self.map.has_indicators {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let mut new = Leds::none();
|
|
||||||
macro_rules! map_led {
|
|
||||||
($field:ident, $led:ident) => {
|
|
||||||
if let Some(m) = &self.map.$field
|
|
||||||
&& m.matches(&self.mods)
|
|
||||||
{
|
|
||||||
new |= $led;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
map_led!(num_lock, LED_NUM_LOCK);
|
|
||||||
map_led!(caps_lock, LED_CAPS_LOCK);
|
|
||||||
map_led!(scroll_lock, LED_SCROLL_LOCK);
|
|
||||||
map_led!(compose, LED_COMPOSE);
|
|
||||||
map_led!(kana, LED_KANA);
|
|
||||||
if new != self.leds {
|
|
||||||
self.leds = new;
|
|
||||||
for listener in self.leds_changed.iter() {
|
|
||||||
listener.leds(new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct KeymapFd {
|
|
||||||
pub map: Rc<OwnedFd>,
|
|
||||||
pub len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeymapFd {
|
|
||||||
pub fn create_unprotected_fd(&self) -> Result<Self, KeyboardError> {
|
|
||||||
let fd = uapi::memfd_create("shared-keymap", c::MFD_CLOEXEC)
|
|
||||||
.map_os_err(KeyboardError::KeymapMemfd)?;
|
|
||||||
let target = self.len as c::off_t;
|
|
||||||
let mut pos = 0;
|
|
||||||
while pos < target {
|
|
||||||
let rem = target - pos;
|
|
||||||
let res = uapi::sendfile(fd.raw(), self.map.raw(), Some(&mut pos), rem as usize)
|
|
||||||
.to_os_error();
|
|
||||||
match res {
|
|
||||||
Ok(_) | Err(OsError(c::EINTR)) => {}
|
|
||||||
Err(e) => return Err(KeyboardError::KeymapCopy(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
map: Rc::new(fd),
|
|
||||||
len: self.len,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KbvmContext {
|
|
||||||
pub fn parse_keymap(&self, keymap: &[u8]) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
||||||
let map = self
|
|
||||||
.ctx
|
|
||||||
.keymap_from_bytes(WriteToLog, None, keymap)
|
|
||||||
.map_err(KbvmError::CouldNotParseKeymap)?;
|
|
||||||
let id = KbvmMapId(*blake3::hash(keymap).as_bytes());
|
|
||||||
self.create_keymap(id, map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keymap_from_rmlvo(
|
|
||||||
&self,
|
|
||||||
rules: Option<&str>,
|
|
||||||
model: Option<&str>,
|
|
||||||
layout: Option<&str>,
|
|
||||||
variant: Option<&str>,
|
|
||||||
options: Option<&str>,
|
|
||||||
) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
||||||
let mut groups = None::<Vec<_>>;
|
|
||||||
if layout.is_some() || variant.is_some() {
|
|
||||||
groups = Some(
|
|
||||||
Group::from_layouts_and_variants(
|
|
||||||
layout.unwrap_or_default(),
|
|
||||||
variant.unwrap_or_default(),
|
|
||||||
)
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let mut options_vec = None::<Vec<_>>;
|
|
||||||
if let Some(options) = options {
|
|
||||||
options_vec = Some(options.split(",").collect());
|
|
||||||
}
|
|
||||||
self.keymap_from_names(rules, model, groups.as_deref(), options_vec.as_deref())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keymap_from_names(
|
|
||||||
&self,
|
|
||||||
rules: Option<&str>,
|
|
||||||
model: Option<&str>,
|
|
||||||
groups: Option<&[Group<'_>]>,
|
|
||||||
options: Option<&[&str]>,
|
|
||||||
) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
||||||
let map = self
|
|
||||||
.ctx
|
|
||||||
.keymap_from_names(WriteToLog, rules, model, groups, options);
|
|
||||||
let id = KbvmMapId(*blake3::hash(map.format().to_string().as_bytes()).as_bytes());
|
|
||||||
self.create_keymap(id, map)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_keymap(&self, id: KbvmMapId, map: Keymap) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
||||||
let mut has_indicators = false;
|
|
||||||
let mut num_lock = None;
|
|
||||||
let mut caps_lock = None;
|
|
||||||
let mut scroll_lock = None;
|
|
||||||
let mut compose = None;
|
|
||||||
let mut kana = None;
|
|
||||||
for indicator in map.indicators() {
|
|
||||||
match indicator.name() {
|
|
||||||
Indicator::NUM_LOCK => num_lock = Some(indicator.matcher()),
|
|
||||||
Indicator::CAPS_LOCK => caps_lock = Some(indicator.matcher()),
|
|
||||||
Indicator::SCROLL_LOCK => scroll_lock = Some(indicator.matcher()),
|
|
||||||
Indicator::COMPOSE => compose = Some(indicator.matcher()),
|
|
||||||
Indicator::KANA => kana = Some(indicator.matcher()),
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
has_indicators = true;
|
|
||||||
}
|
|
||||||
let builder = map.to_builder();
|
|
||||||
let (_, xwayland_map) = create_keymap_memfd(&map, true).map_err(KbvmError::KeymapMemfd)?;
|
|
||||||
let (_, map) = create_keymap_memfd(&map, false).map_err(KbvmError::KeymapMemfd)?;
|
|
||||||
Ok(Rc::new(KbvmMap {
|
|
||||||
id,
|
|
||||||
state_machine: builder.build_state_machine(),
|
|
||||||
map,
|
|
||||||
xwayland_map,
|
|
||||||
lookup_table: builder.build_lookup_table(),
|
|
||||||
has_indicators,
|
|
||||||
num_lock,
|
|
||||||
caps_lock,
|
|
||||||
scroll_lock,
|
|
||||||
compose,
|
|
||||||
kana,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_keymap_memfd(map: &Keymap, xwayland: bool) -> Result<(String, KeymapFd), OsError> {
|
|
||||||
let mut format = map.format();
|
|
||||||
if xwayland {
|
|
||||||
format = format.lookup_only(true).rename_long_keys(true);
|
|
||||||
}
|
|
||||||
let str = format!("{}\n", format);
|
|
||||||
let mut memfd =
|
|
||||||
uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).to_os_error()?;
|
|
||||||
memfd.write_all(str.as_bytes())?;
|
|
||||||
memfd.write_all(&[0])?;
|
|
||||||
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).to_os_error()?;
|
|
||||||
uapi::fcntl_add_seals(
|
|
||||||
memfd.raw(),
|
|
||||||
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
|
||||||
)
|
|
||||||
.to_os_error()?;
|
|
||||||
let fd = KeymapFd {
|
|
||||||
map: Rc::new(memfd),
|
|
||||||
len: str.len() + 1,
|
|
||||||
};
|
|
||||||
Ok((str, fd))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KbvmMap {
|
|
||||||
pub fn state(self: &Rc<Self>, id: KeyboardStateId) -> KbvmState {
|
|
||||||
KbvmState {
|
|
||||||
map: self.clone(),
|
|
||||||
state: self.state_machine.create_state(),
|
|
||||||
kb_state: KeyboardState {
|
|
||||||
id,
|
|
||||||
map: self.clone(),
|
|
||||||
pressed_keys: Default::default(),
|
|
||||||
mods: Default::default(),
|
|
||||||
leds: Default::default(),
|
|
||||||
leds_changed: Default::default(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KbvmState {
|
|
||||||
pub fn apply_events(&mut self, events: &SyncQueue<Event>) {
|
|
||||||
let state = &mut self.kb_state;
|
|
||||||
while let Some(event) = events.pop() {
|
|
||||||
state.apply_event(event);
|
|
||||||
match event {
|
|
||||||
Event::KeyDown(kc) => {
|
|
||||||
state.pressed_keys.insert(kc.to_evdev());
|
|
||||||
}
|
|
||||||
Event::KeyUp(kc) => {
|
|
||||||
state.pressed_keys.remove(&kc.to_evdev());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-layout-animation"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Layout animation planning for Jay"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-geometry = { path = "../geometry" }
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,21 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-libinput"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
|
||||||
isnt = "0.2.0"
|
|
||||||
libloading = "0.9.0"
|
|
||||||
log = { version = "0.4.20", features = ["std"] }
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
anyhow = "1.0.79"
|
|
||||||
cc = "1.0.86"
|
|
||||||
repc = "0.1.1"
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
use {
|
|
||||||
repc::layout::{Type, TypeVariant},
|
|
||||||
std::{
|
|
||||||
env,
|
|
||||||
fs::{File, OpenOptions},
|
|
||||||
io::{self, BufWriter, Write},
|
|
||||||
path::PathBuf,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! cenum {
|
|
||||||
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct $name(pub i32);
|
|
||||||
|
|
||||||
impl $name {
|
|
||||||
pub fn raw(self) -> i32 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub const $name2: $name = $name($val);
|
|
||||||
)*
|
|
||||||
|
|
||||||
pub const $uc: &[i32] = &[$($val,)*];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[path = "src/consts.rs"]
|
|
||||||
mod consts;
|
|
||||||
|
|
||||||
fn open(s: &str) -> io::Result<BufWriter<File>> {
|
|
||||||
let mut path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
||||||
path.push(s);
|
|
||||||
Ok(BufWriter::new(
|
|
||||||
OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_target() -> repc::Target {
|
|
||||||
let rustc_target = env::var("TARGET").unwrap();
|
|
||||||
repc::TARGET_MAP
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.find(|t| t.0 == rustc_target)
|
|
||||||
.unwrap()
|
|
||||||
.1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_enum_ty(variants: Vec<i128>) -> anyhow::Result<u64> {
|
|
||||||
let target = get_target();
|
|
||||||
let ty = Type {
|
|
||||||
layout: (),
|
|
||||||
annotations: vec![],
|
|
||||||
variant: TypeVariant::Enum(variants),
|
|
||||||
};
|
|
||||||
let ty = repc::compute_layout(target, &ty)?;
|
|
||||||
assert!(ty.layout.pointer_alignment_bits <= ty.layout.size_bits);
|
|
||||||
Ok(ty.layout.size_bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_ty<W: Write>(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> {
|
|
||||||
let variants: Vec<_> = vals.iter().cloned().map(|v| v as i128).collect();
|
|
||||||
let size = get_enum_ty(variants)?;
|
|
||||||
writeln!(f, "#[allow(clippy::allow_attributes, dead_code)]")?;
|
|
||||||
writeln!(f, "pub type {} = i{};", ty, size)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
println!("cargo:rerun-if-changed=src/bridge.c");
|
|
||||||
cc::Build::new()
|
|
||||||
.file("src/bridge.c")
|
|
||||||
.opt_level(2)
|
|
||||||
.compile("jay-libinput-bridge");
|
|
||||||
|
|
||||||
let mut f = open("libinput_tys.rs")?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_LOG_PRIORITY,
|
|
||||||
"libinput_log_priority",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_DEVICE_CAPABILITY,
|
|
||||||
"libinput_device_capability",
|
|
||||||
)?;
|
|
||||||
write_ty(&mut f, consts::LIBINPUT_KEY_STATE, "libinput_key_state")?;
|
|
||||||
write_ty(&mut f, consts::LIBINPUT_LED, "libinput_led")?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_BUTTON_STATE,
|
|
||||||
"libinput_button_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_POINTER_AXIS,
|
|
||||||
"libinput_pointer_axis",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_POINTER_AXIS_SOURCE,
|
|
||||||
"libinput_pointer_axis_source",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_TABLET_PAD_RING_AXIS_SOURCE,
|
|
||||||
"libinput_tablet_pad_ring_axis_source",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_TABLET_PAD_STRIP_AXIS_SOURCE,
|
|
||||||
"libinput_tablet_pad_strip_axis_source",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_TABLET_TOOL_TYPE,
|
|
||||||
"libinput_tablet_tool_type",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_TABLET_TOOL_PROXIMITY_STATE,
|
|
||||||
"libinput_tablet_tool_proximity_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_TABLET_TOOL_TIP_STATE,
|
|
||||||
"libinput_tablet_tool_tip_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_SWITCH_STATE,
|
|
||||||
"libinput_switch_state",
|
|
||||||
)?;
|
|
||||||
write_ty(&mut f, consts::LIBINPUT_SWITCH, "libinput_switch")?;
|
|
||||||
write_ty(&mut f, consts::LIBINPUT_EVENT_TYPE, "libinput_event_type")?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_STATUS,
|
|
||||||
"libinput_config_status",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_ACCEL_PROFILE,
|
|
||||||
"libinput_config_accel_profile",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_TAP_STATE,
|
|
||||||
"libinput_config_tap_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_DRAG_STATE,
|
|
||||||
"libinput_config_drag_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_DRAG_LOCK_STATE,
|
|
||||||
"libinput_config_drag_lock_state",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_CLICK_METHOD,
|
|
||||||
"libinput_config_click_method",
|
|
||||||
)?;
|
|
||||||
write_ty(
|
|
||||||
&mut f,
|
|
||||||
consts::LIBINPUT_CONFIG_MIDDLE_EMULATION_STATE,
|
|
||||||
"libinput_config_middle_emulation_state",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=src/consts.rs");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
|
|
||||||
macro_rules! cenum {
|
|
||||||
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct $name(pub i32);
|
|
||||||
|
|
||||||
impl $name {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn raw(self) -> i32 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub const $name2: $name = $name($val);
|
|
||||||
)*
|
|
||||||
|
|
||||||
pub const $uc: &[i32] = &[$($val,)*];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod consts;
|
|
||||||
pub mod device;
|
|
||||||
pub mod event;
|
|
||||||
mod sys;
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
consts::{
|
|
||||||
LIBINPUT_LOG_PRIORITY_DEBUG, LIBINPUT_LOG_PRIORITY_ERROR, LIBINPUT_LOG_PRIORITY_INFO,
|
|
||||||
LogPriority,
|
|
||||||
},
|
|
||||||
device::RegisteredDevice,
|
|
||||||
event::LibInputEvent,
|
|
||||||
sys::{
|
|
||||||
libinput, libinput_device_ref, libinput_dispatch, libinput_get_event, libinput_get_fd,
|
|
||||||
libinput_interface, libinput_log_priority, libinput_log_set_handler,
|
|
||||||
libinput_log_set_priority, libinput_path_add_device, libinput_path_create_context,
|
|
||||||
libinput_unref,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
bstr::ByteSlice,
|
|
||||||
isnt::std_1::primitive::IsntConstPtrExt,
|
|
||||||
jay_utils::{errorfmt::ErrorFmt, oserror::OsError, ptr_ext::PtrExt},
|
|
||||||
std::{ffi::CStr, rc::Rc},
|
|
||||||
thiserror::Error,
|
|
||||||
uapi::{IntoUstr, OwnedFd, c},
|
|
||||||
};
|
|
||||||
|
|
||||||
static INTERFACE: libinput_interface = libinput_interface {
|
|
||||||
open_restricted,
|
|
||||||
close_restricted,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe extern "C" fn open_restricted(
|
|
||||||
path: *const c::c_char,
|
|
||||||
_flags: c::c_int,
|
|
||||||
user_data: *mut c::c_void,
|
|
||||||
) -> c::c_int {
|
|
||||||
unsafe {
|
|
||||||
let ud = (user_data as *const UserData).deref();
|
|
||||||
match ud.adapter.open(CStr::from_ptr(path)) {
|
|
||||||
Ok(f) => f.unwrap(),
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("Could not open device for libinput: {}", ErrorFmt(e));
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn close_restricted(fd: c::c_int, _user_data: *mut c::c_void) {
|
|
||||||
drop(OwnedFd::new(fd));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UserData {
|
|
||||||
adapter: Rc<dyn LibInputAdapter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait LibInputAdapter {
|
|
||||||
fn open(&self, path: &CStr) -> Result<OwnedFd, LibInputError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum LibInputError {
|
|
||||||
#[error("Could not create a libinput instance")]
|
|
||||||
New,
|
|
||||||
#[error("Could not open a libinput device")]
|
|
||||||
Open,
|
|
||||||
#[error("Could not dispatch libinput events")]
|
|
||||||
Dispatch(#[source] OsError),
|
|
||||||
#[error("The requested device is not available")]
|
|
||||||
DeviceUnavailable,
|
|
||||||
#[error("Dupfd failed")]
|
|
||||||
DupFd(#[source] OsError),
|
|
||||||
#[error("Stat failed")]
|
|
||||||
Stat(#[source] OsError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LibInput {
|
|
||||||
_data: Box<UserData>,
|
|
||||||
li: *mut libinput,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn jay_libinput_log_handler_bridge();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LibInput {
|
|
||||||
pub fn new(adapter: Rc<dyn LibInputAdapter>) -> Result<Self, LibInputError> {
|
|
||||||
let mut ud = Box::new(UserData { adapter });
|
|
||||||
let li = unsafe {
|
|
||||||
libinput_path_create_context(&INTERFACE, &mut *ud as *mut _ as *mut c::c_void)
|
|
||||||
};
|
|
||||||
if li.is_null() {
|
|
||||||
return Err(LibInputError::New);
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
libinput_log_set_handler(li, jay_libinput_log_handler_bridge);
|
|
||||||
let priority = if log::log_enabled!(log::Level::Debug) {
|
|
||||||
LIBINPUT_LOG_PRIORITY_DEBUG
|
|
||||||
} else if log::log_enabled!(log::Level::Info) {
|
|
||||||
LIBINPUT_LOG_PRIORITY_INFO
|
|
||||||
} else {
|
|
||||||
LIBINPUT_LOG_PRIORITY_ERROR
|
|
||||||
};
|
|
||||||
libinput_log_set_priority(li, priority.raw() as _);
|
|
||||||
}
|
|
||||||
Ok(Self { _data: ud, li })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> c::c_int {
|
|
||||||
unsafe { libinput_get_fd(self.li) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open<'a>(
|
|
||||||
self: &Rc<Self>,
|
|
||||||
path: impl IntoUstr<'a>,
|
|
||||||
) -> Result<RegisteredDevice, LibInputError> {
|
|
||||||
let path = path.into_ustr();
|
|
||||||
let res = unsafe { libinput_path_add_device(self.li, path.as_ptr()) };
|
|
||||||
if res.is_null() {
|
|
||||||
Err(LibInputError::Open)
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
libinput_device_ref(res);
|
|
||||||
}
|
|
||||||
Ok(RegisteredDevice {
|
|
||||||
_li: self.clone(),
|
|
||||||
dev: res,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dispatch(&self) -> Result<(), LibInputError> {
|
|
||||||
let res = unsafe { libinput_dispatch(self.li) };
|
|
||||||
if res < 0 {
|
|
||||||
Err(LibInputError::Dispatch(OsError(-res)))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn event(&self) -> Option<LibInputEvent<'_>> {
|
|
||||||
let res = unsafe { libinput_get_event(self.li) };
|
|
||||||
if res.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(LibInputEvent {
|
|
||||||
event: res,
|
|
||||||
_phantom: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LibInput {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
libinput_unref(self.li);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
unsafe extern "C" fn jay_libinput_log_handler(
|
|
||||||
_libinput: *mut libinput,
|
|
||||||
priority: libinput_log_priority,
|
|
||||||
line: *const c::c_char,
|
|
||||||
) {
|
|
||||||
assert!(line.is_not_null());
|
|
||||||
let str = unsafe { CStr::from_ptr(line) };
|
|
||||||
let priority = match LogPriority(priority as _) {
|
|
||||||
LIBINPUT_LOG_PRIORITY_DEBUG => log::Level::Debug,
|
|
||||||
LIBINPUT_LOG_PRIORITY_INFO => log::Level::Info,
|
|
||||||
LIBINPUT_LOG_PRIORITY_ERROR => log::Level::Error,
|
|
||||||
_ => log::Level::Error,
|
|
||||||
};
|
|
||||||
log::log!(
|
|
||||||
priority,
|
|
||||||
"libinput: {}",
|
|
||||||
str.to_bytes().trim_ascii().as_bstr()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-logger"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-config = { path = "../jay-config" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
backtrace = "0.3.69"
|
|
||||||
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
|
||||||
clap = { version = "4.4.18", features = ["derive", "wrap_help"] }
|
|
||||||
dirs = "6.0.0"
|
|
||||||
humantime = "2.1.0"
|
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
|
||||||
log = { version = "0.4.20", features = ["std"] }
|
|
||||||
parking_lot = "0.12.1"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-output-schedule"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-async-engine = { path = "../async-engine" }
|
|
||||||
jay-io-uring = { path = "../io-uring" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
futures-util = "0.3.30"
|
|
||||||
log = "0.4.20"
|
|
||||||
num-traits = "0.2.17"
|
|
||||||
|
|
@ -1,219 +0,0 @@
|
||||||
use {
|
|
||||||
jay_async_engine::AsyncEngine,
|
|
||||||
jay_io_uring::{IoUring, IoUringError},
|
|
||||||
jay_utils::{
|
|
||||||
asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt,
|
|
||||||
numcell::NumCell,
|
|
||||||
},
|
|
||||||
futures_util::{FutureExt, select},
|
|
||||||
num_traits::ToPrimitive,
|
|
||||||
std::{cell::Cell, rc::Rc},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
enum Change {
|
|
||||||
/// The backend has applied the latest changes.
|
|
||||||
None,
|
|
||||||
/// There are changes that the backend is not yet aware of.
|
|
||||||
Scheduled,
|
|
||||||
/// The backend is aware that there are changes and will apply them as part of the
|
|
||||||
/// next latch event.
|
|
||||||
AwaitingLatch,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OutputSchedule {
|
|
||||||
changed: AsyncEvent,
|
|
||||||
run: Cell<bool>,
|
|
||||||
|
|
||||||
damage_connector: Rc<dyn Fn()>,
|
|
||||||
hardware_cursor_damage: CloneCell<Option<Rc<dyn Fn()>>>,
|
|
||||||
cursor_hz_changed: Rc<dyn Fn(Option<f64>)>,
|
|
||||||
|
|
||||||
persistent: Rc<dyn OutputSchedulePersistent>,
|
|
||||||
|
|
||||||
last_present_nsec: Cell<u64>,
|
|
||||||
cursor_delta_nsec: Cell<Option<u64>>,
|
|
||||||
|
|
||||||
ring: Rc<IoUring>,
|
|
||||||
eng: Rc<AsyncEngine>,
|
|
||||||
|
|
||||||
vrr_enabled: Cell<bool>,
|
|
||||||
|
|
||||||
hardware_cursor_change: Cell<Change>,
|
|
||||||
software_cursor_change: Cell<Change>,
|
|
||||||
|
|
||||||
iteration: NumCell<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait OutputSchedulePersistent {
|
|
||||||
fn vrr_cursor_hz(&self) -> Option<f64>;
|
|
||||||
fn set_vrr_cursor_hz(&self, hz: Option<f64>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputSchedule {
|
|
||||||
pub fn new(
|
|
||||||
ring: Rc<IoUring>,
|
|
||||||
eng: Rc<AsyncEngine>,
|
|
||||||
persistent: Rc<dyn OutputSchedulePersistent>,
|
|
||||||
damage_connector: Rc<dyn Fn()>,
|
|
||||||
cursor_hz_changed: Rc<dyn Fn(Option<f64>)>,
|
|
||||||
) -> Self {
|
|
||||||
let slf = Self {
|
|
||||||
changed: Default::default(),
|
|
||||||
run: Default::default(),
|
|
||||||
damage_connector,
|
|
||||||
cursor_hz_changed,
|
|
||||||
ring,
|
|
||||||
eng,
|
|
||||||
vrr_enabled: Default::default(),
|
|
||||||
hardware_cursor_change: Cell::new(Change::None),
|
|
||||||
software_cursor_change: Cell::new(Change::None),
|
|
||||||
hardware_cursor_damage: Default::default(),
|
|
||||||
persistent: persistent.clone(),
|
|
||||||
last_present_nsec: Default::default(),
|
|
||||||
cursor_delta_nsec: Default::default(),
|
|
||||||
iteration: Default::default(),
|
|
||||||
};
|
|
||||||
if let Some(hz) = persistent.vrr_cursor_hz() {
|
|
||||||
slf.set_cursor_hz(hz);
|
|
||||||
}
|
|
||||||
slf
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn drive(self: Rc<Self>) {
|
|
||||||
loop {
|
|
||||||
self.run_once().await;
|
|
||||||
while !self.run.take() {
|
|
||||||
self.changed.triggered().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trigger(&self) {
|
|
||||||
let trigger = self.vrr_enabled.get()
|
|
||||||
&& self.cursor_delta_nsec.is_some()
|
|
||||||
&& (self.software_cursor_change.get() == Change::Scheduled
|
|
||||||
|| self.hardware_cursor_change.get() == Change::Scheduled);
|
|
||||||
if trigger {
|
|
||||||
self.run.set(true);
|
|
||||||
self.changed.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn latched(&self) {
|
|
||||||
self.last_present_nsec.set(self.eng.now().nsec());
|
|
||||||
if self.software_cursor_change.get() == Change::AwaitingLatch {
|
|
||||||
self.software_cursor_change.set(Change::None);
|
|
||||||
}
|
|
||||||
if self.hardware_cursor_change.get() == Change::AwaitingLatch {
|
|
||||||
self.hardware_cursor_change.set(Change::None);
|
|
||||||
}
|
|
||||||
self.iteration.fetch_add(1);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vrr_enabled(&self) -> bool {
|
|
||||||
self.vrr_enabled.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_vrr_enabled(&self, enabled: bool) {
|
|
||||||
self.vrr_enabled.set(enabled);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursor_hz(&self, hz: f64) {
|
|
||||||
let (hz, delta) = match map_cursor_hz(hz) {
|
|
||||||
None => {
|
|
||||||
log::warn!("Ignoring cursor frequency {hz}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some(v) => v,
|
|
||||||
};
|
|
||||||
self.persistent.set_vrr_cursor_hz(hz);
|
|
||||||
(self.cursor_hz_changed)(hz);
|
|
||||||
self.cursor_delta_nsec.set(delta);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_hardware_cursor_damage(&self, damage: &Option<Rc<dyn Fn()>>) {
|
|
||||||
self.hardware_cursor_damage.set(damage.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defer_cursor_updates(&self) -> bool {
|
|
||||||
self.vrr_enabled.get() && self.cursor_delta_nsec.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hardware_cursor_changed(&self) {
|
|
||||||
if self.hardware_cursor_change.get() == Change::None {
|
|
||||||
self.hardware_cursor_change.set(Change::Scheduled);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn software_cursor_changed(&self) {
|
|
||||||
if self.software_cursor_change.get() == Change::None {
|
|
||||||
self.software_cursor_change.set(Change::Scheduled);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_once(&self) {
|
|
||||||
loop {
|
|
||||||
if self.hardware_cursor_change.get() != Change::Scheduled
|
|
||||||
&& self.software_cursor_change.get() != Change::Scheduled
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !self.vrr_enabled.get() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Some(duration) = self.cursor_delta_nsec.get() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let iteration = self.iteration.get();
|
|
||||||
let next_present = self.last_present_nsec.get().saturating_add(duration);
|
|
||||||
let res: Result<(), IoUringError> = select! {
|
|
||||||
_ = self.changed.triggered().fuse() => continue,
|
|
||||||
v = self.ring.timeout(next_present).fuse() => v,
|
|
||||||
};
|
|
||||||
if let Err(e) = res {
|
|
||||||
log::error!("Could not wait for timer to expire: {}", ErrorFmt(e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if iteration == self.iteration.get() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.commit_cursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn commit_cursor(&self) {
|
|
||||||
if self.hardware_cursor_change.get() == Change::Scheduled {
|
|
||||||
if let Some(damage) = self.hardware_cursor_damage.get() {
|
|
||||||
damage();
|
|
||||||
}
|
|
||||||
self.hardware_cursor_change.set(Change::AwaitingLatch);
|
|
||||||
}
|
|
||||||
if self.software_cursor_change.get() == Change::Scheduled {
|
|
||||||
(self.damage_connector)();
|
|
||||||
self.software_cursor_change.set(Change::AwaitingLatch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_cursor_hz(hz: f64) -> Option<(Option<f64>, Option<u64>)> {
|
|
||||||
if hz <= 0.0 {
|
|
||||||
return Some((Some(0.0), Some(u64::MAX)));
|
|
||||||
}
|
|
||||||
let delta = (1_000_000_000.0 / hz).to_u64();
|
|
||||||
if delta.is_none() {
|
|
||||||
if hz > 0.0 {
|
|
||||||
return Some((None, None));
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if delta == Some(0) {
|
|
||||||
return Some((None, None));
|
|
||||||
}
|
|
||||||
Some((Some(hz), delta))
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-output-types"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Output identity types for the Jay compositor"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-cmm = { path = "../cmm" }
|
|
||||||
jay-formats = { path = "../formats" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
blake3 = "1.8.2"
|
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,348 +0,0 @@
|
||||||
use {
|
|
||||||
jay_cmm::cmm_primaries::Primaries,
|
|
||||||
jay_formats::Format,
|
|
||||||
jay_utils::numcell::NumCell,
|
|
||||||
linearize::Linearize,
|
|
||||||
std::{
|
|
||||||
fmt::{self, Debug, Display, Formatter},
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
ops::{BitOr, BitOrAssign},
|
|
||||||
rc::Rc,
|
|
||||||
},
|
|
||||||
uapi::{Packed, Pod},
|
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
linear_ids!(ConnectorIds, ConnectorId);
|
|
||||||
linear_ids!(DrmDeviceIds, DrmDeviceId);
|
|
||||||
linear_ids!(
|
|
||||||
BackendConnectorStateSerials,
|
|
||||||
BackendConnectorStateSerial,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
|
||||||
pub struct ConnectorCaps(pub u32);
|
|
||||||
|
|
||||||
pub const CONCAP_CONNECTOR: ConnectorCaps = ConnectorCaps(1 << 0);
|
|
||||||
pub const CONCAP_MODE_SETTING: ConnectorCaps = ConnectorCaps(1 << 1);
|
|
||||||
pub const CONCAP_PHYSICAL_DISPLAY: ConnectorCaps = ConnectorCaps(1 << 2);
|
|
||||||
|
|
||||||
impl ConnectorCaps {
|
|
||||||
pub fn none() -> Self {
|
|
||||||
Self(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(self, other: Self) -> bool {
|
|
||||||
self.0 & other.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOr for ConnectorCaps {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn bitor(self, rhs: Self) -> Self::Output {
|
|
||||||
Self(self.0 | rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOrAssign for ConnectorCaps {
|
|
||||||
fn bitor_assign(&mut self, rhs: Self) {
|
|
||||||
self.0 |= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ConnectorCaps {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
let mut any = false;
|
|
||||||
let mut v = self.0;
|
|
||||||
for (cap, name) in [
|
|
||||||
(CONCAP_CONNECTOR, "CONCAP_CONNECTOR"),
|
|
||||||
(CONCAP_MODE_SETTING, "CONCAP_MODE_SETTING"),
|
|
||||||
(CONCAP_PHYSICAL_DISPLAY, "CONCAP_PHYSICAL_DISPLAY"),
|
|
||||||
] {
|
|
||||||
if v & cap.0 == cap.0 {
|
|
||||||
if any {
|
|
||||||
write!(f, "|")?;
|
|
||||||
}
|
|
||||||
any = true;
|
|
||||||
write!(f, "{}", name)?;
|
|
||||||
v &= !cap.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !any || v != 0 {
|
|
||||||
if any {
|
|
||||||
write!(f, "|")?;
|
|
||||||
}
|
|
||||||
write!(f, "0x{:x}", v)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
|
||||||
pub struct Mode {
|
|
||||||
pub width: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub refresh_rate_millihz: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mode {
|
|
||||||
pub fn refresh_nsec(&self) -> u64 {
|
|
||||||
match self.refresh_rate_millihz {
|
|
||||||
0 => u64::MAX,
|
|
||||||
n => 1_000_000_000_000 / (n as u64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> (i32, i32) {
|
|
||||||
(self.width, self.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Mode {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}x{}@{}",
|
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
self.refresh_rate_millihz as f64 / 1000.0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Linearize)]
|
|
||||||
pub enum BackendEotfs {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Pq,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Linearize)]
|
|
||||||
pub enum BackendColorSpace {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Bt2020,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct BackendLuminance {
|
|
||||||
pub min: f64,
|
|
||||||
pub max: f64,
|
|
||||||
pub max_fall: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BackendEotfs {
|
|
||||||
pub fn to_drm(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
BackendEotfs::Default => 0,
|
|
||||||
BackendEotfs::Pq => 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn name(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
BackendEotfs::Default => "default",
|
|
||||||
BackendEotfs::Pq => "pq",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BackendColorSpace {
|
|
||||||
pub fn to_drm(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
BackendColorSpace::Default => 0,
|
|
||||||
BackendColorSpace::Bt2020 => 9,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn name(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
BackendColorSpace::Default => "default",
|
|
||||||
BackendColorSpace::Bt2020 => "bt2020",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// kernel: struct drm_color_lut
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct BackendGammaLutElement {
|
|
||||||
pub red: u16,
|
|
||||||
pub green: u16,
|
|
||||||
pub blue: u16,
|
|
||||||
pub reserved: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Pod for BackendGammaLutElement {}
|
|
||||||
unsafe impl Packed for BackendGammaLutElement {}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq)]
|
|
||||||
pub struct BackendGammaLut {
|
|
||||||
id: [u8; 32],
|
|
||||||
pub gamma_lut: Vec<BackendGammaLutElement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BackendGammaLut {
|
|
||||||
pub fn new(mut gamma_lut: Vec<BackendGammaLutElement>) -> Self {
|
|
||||||
for element in &mut gamma_lut {
|
|
||||||
element.reserved = 0;
|
|
||||||
}
|
|
||||||
let gamma_lut_bytes = uapi::as_bytes(&gamma_lut as &[_]);
|
|
||||||
let id = *blake3::hash(gamma_lut_bytes).as_bytes();
|
|
||||||
Self { id, gamma_lut }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for BackendGammaLut {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct BackendConnectorState {
|
|
||||||
pub serial: BackendConnectorStateSerial,
|
|
||||||
pub enabled: bool,
|
|
||||||
pub active: bool,
|
|
||||||
pub mode: Mode,
|
|
||||||
pub non_desktop_override: Option<bool>,
|
|
||||||
pub vrr: bool,
|
|
||||||
pub tearing: bool,
|
|
||||||
pub format: &'static Format,
|
|
||||||
pub color_space: BackendColorSpace,
|
|
||||||
pub eotf: BackendEotfs,
|
|
||||||
pub gamma_lut: Option<Rc<BackendGammaLut>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct MonitorInfo {
|
|
||||||
pub modes: Option<Vec<Mode>>,
|
|
||||||
pub output_id: Rc<OutputId>,
|
|
||||||
pub width_mm: i32,
|
|
||||||
pub height_mm: i32,
|
|
||||||
pub non_desktop: bool,
|
|
||||||
pub non_desktop_effective: bool,
|
|
||||||
pub vrr_capable: bool,
|
|
||||||
pub eotfs: Vec<BackendEotfs>,
|
|
||||||
pub color_spaces: Vec<BackendColorSpace>,
|
|
||||||
pub primaries: Primaries,
|
|
||||||
pub luminance: Option<BackendLuminance>,
|
|
||||||
pub state: BackendConnectorState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
|
||||||
pub struct OutputIdHash(pub [u8; 32]);
|
|
||||||
|
|
||||||
impl OutputIdHash {
|
|
||||||
pub fn hash(t: impl AsRef<[u8]>) -> Self {
|
|
||||||
Self(*blake3::hash(t.as_ref()).as_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, Debug)]
|
|
||||||
pub struct OutputId {
|
|
||||||
pub _connector: Option<String>,
|
|
||||||
pub manufacturer: String,
|
|
||||||
pub model: String,
|
|
||||||
pub serial_number: String,
|
|
||||||
pub hash: OutputIdHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for OutputId {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.hash == other.hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for OutputId {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.hash.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputId {
|
|
||||||
pub fn new(
|
|
||||||
connector: impl Into<String>,
|
|
||||||
manufacturer: impl Into<String>,
|
|
||||||
model: impl Into<String>,
|
|
||||||
serial_number: impl Into<String>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
let connector = connector.into();
|
|
||||||
let manufacturer = manufacturer.into();
|
|
||||||
let model = model.into();
|
|
||||||
let serial_number = serial_number.into();
|
|
||||||
Self::new_(connector, manufacturer, model, serial_number)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_(
|
|
||||||
connector: String,
|
|
||||||
manufacturer: String,
|
|
||||||
model: String,
|
|
||||||
serial_number: String,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
let connector = serial_number.is_empty().then_some(connector);
|
|
||||||
let mut hasher = blake3::Hasher::new();
|
|
||||||
hasher.update(&[connector.is_some() as u8]);
|
|
||||||
let mut hash = |s: &str| {
|
|
||||||
hasher.update(&(s.len() as u64).to_le_bytes());
|
|
||||||
hasher.update(s.as_bytes());
|
|
||||||
};
|
|
||||||
connector.as_deref().map(&mut hash);
|
|
||||||
hash(&manufacturer);
|
|
||||||
hash(&model);
|
|
||||||
hash(&serial_number);
|
|
||||||
Rc::new(Self {
|
|
||||||
_connector: connector,
|
|
||||||
manufacturer,
|
|
||||||
model,
|
|
||||||
serial_number,
|
|
||||||
hash: OutputIdHash(*hasher.finalize().as_bytes()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-pango"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-geometry = { path = "../geometry" }
|
|
||||||
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
anyhow = "1.0.79"
|
|
||||||
repc = "0.1.1"
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
use {
|
|
||||||
repc::layout::{Type, TypeVariant},
|
|
||||||
std::{
|
|
||||||
env,
|
|
||||||
fs::{File, OpenOptions},
|
|
||||||
io::{self, BufWriter, Write},
|
|
||||||
path::PathBuf,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! cenum {
|
|
||||||
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct $name(pub i32);
|
|
||||||
|
|
||||||
impl $name {
|
|
||||||
pub fn raw(self) -> i32 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub const $name2: $name = $name($val);
|
|
||||||
)*
|
|
||||||
|
|
||||||
pub const $uc: &[i32] = &[$($val,)*];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[path = "src/consts.rs"]
|
|
||||||
mod consts;
|
|
||||||
|
|
||||||
fn open(s: &str) -> io::Result<BufWriter<File>> {
|
|
||||||
let mut path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
||||||
path.push(s);
|
|
||||||
Ok(BufWriter::new(
|
|
||||||
OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_target() -> repc::Target {
|
|
||||||
let rustc_target = env::var("TARGET").unwrap();
|
|
||||||
repc::TARGET_MAP
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.find(|t| t.0 == rustc_target)
|
|
||||||
.unwrap()
|
|
||||||
.1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_enum_ty(variants: Vec<i128>) -> anyhow::Result<u64> {
|
|
||||||
let target = get_target();
|
|
||||||
let ty = Type {
|
|
||||||
layout: (),
|
|
||||||
annotations: vec![],
|
|
||||||
variant: TypeVariant::Enum(variants),
|
|
||||||
};
|
|
||||||
let ty = repc::compute_layout(target, &ty)?;
|
|
||||||
assert!(ty.layout.pointer_alignment_bits <= ty.layout.size_bits);
|
|
||||||
Ok(ty.layout.size_bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_ty<W: Write>(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> {
|
|
||||||
let variants: Vec<_> = vals.iter().cloned().map(|v| v as i128).collect();
|
|
||||||
let size = get_enum_ty(variants)?;
|
|
||||||
writeln!(f, "#[allow(clippy::allow_attributes, dead_code)]")?;
|
|
||||||
writeln!(f, "pub type {} = i{};", ty, size)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
let mut f = open("pango_tys.rs")?;
|
|
||||||
write_ty(&mut f, consts::CAIRO_FORMATS, "cairo_format_t")?;
|
|
||||||
write_ty(&mut f, consts::CAIRO_STATUSES, "cairo_status_t")?;
|
|
||||||
write_ty(&mut f, consts::CAIRO_OPERATORS, "cairo_operator_t")?;
|
|
||||||
write_ty(&mut f, consts::PANGO_ELLIPSIZE_MODES, "PangoEllipsizeMode_")?;
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=src/consts.rs");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-pr-caps"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
opera = "1.0.1"
|
|
||||||
parking_lot = "0.12.1"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-sighand"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-async-engine = { path = "../async-engine" }
|
|
||||||
jay-io-uring = { path = "../io-uring" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
log = { version = "0.4.20", features = ["std"] }
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-theme"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-cmm = { path = "../cmm" }
|
|
||||||
jay-config = { path = "../jay-config" }
|
|
||||||
jay-gfx-types = { path = "../gfx-types" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
|
||||||
num-traits = "0.2.17"
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-time"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
uapi = "0.2.13"
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
mod context;
|
|
||||||
pub mod error;
|
|
||||||
mod extractor;
|
|
||||||
mod keycodes;
|
|
||||||
mod parser;
|
|
||||||
mod parsers;
|
|
||||||
mod spanned;
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
config::{
|
|
||||||
context::Context,
|
|
||||||
parsers::{
|
|
||||||
config::{ConfigParser, ConfigParserError},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ahash::AHashMap,
|
|
||||||
std::{
|
|
||||||
cell::RefCell,
|
|
||||||
error::Error,
|
|
||||||
},
|
|
||||||
thiserror::Error,
|
|
||||||
jay_toml::toml_parser,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use jay_config_schema::{
|
|
||||||
Action, AnimationCurveConfig, Animations, ClientMatch, ClientRule, ColorManagement, Config,
|
|
||||||
ConfigConnector, ConfigDrmDevice, ConfigKeymap, ConnectorMatch, DrmDeviceMatch, Exec, Float,
|
|
||||||
FocusHistory, GenericMatch, Input, InputMatch, InputMode, Libei, MatchExactly, Mode,
|
|
||||||
NamedAction, Output, OutputMatch, RepeatRate, Scratchpad, Shortcut, SimpleCommand, SimpleIm,
|
|
||||||
Status, Tearing, Theme, UiDrag, Vrr, WindowMatch, WindowRule, Xwayland,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum ConfigError {
|
|
||||||
#[error("Could not parse the toml document")]
|
|
||||||
Toml(#[from] toml_parser::ParserError),
|
|
||||||
#[error("Could not interpret the toml as a config document")]
|
|
||||||
Parser(#[from] ConfigParserError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_config<F>(
|
|
||||||
input: &[u8],
|
|
||||||
mark_names: &RefCell<AHashMap<String, u32>>,
|
|
||||||
handle_error: F,
|
|
||||||
) -> Option<Config>
|
|
||||||
where
|
|
||||||
F: FnOnce(&dyn Error),
|
|
||||||
{
|
|
||||||
let cx = Context {
|
|
||||||
input,
|
|
||||||
used: Default::default(),
|
|
||||||
mark_names,
|
|
||||||
};
|
|
||||||
macro_rules! fatal {
|
|
||||||
($e:expr) => {{
|
|
||||||
let e = ConfigError::from($e.value);
|
|
||||||
let e = cx.error2($e.span, e);
|
|
||||||
handle_error(&e);
|
|
||||||
return None;
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
let toml = match toml_parser::parse(input, &cx) {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(e) => fatal!(e),
|
|
||||||
};
|
|
||||||
let config = match toml.parse(&mut ConfigParser(&cx)) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => fatal!(e),
|
|
||||||
};
|
|
||||||
let used = cx.used.take();
|
|
||||||
macro_rules! check_defined {
|
|
||||||
($name:expr, $used:ident, $defined:ident) => {
|
|
||||||
for spanned in &used.$used {
|
|
||||||
if !used.$defined.contains(spanned) {
|
|
||||||
log::warn!(
|
|
||||||
"{} {} used but not defined: {}",
|
|
||||||
$name,
|
|
||||||
spanned.value,
|
|
||||||
cx.error3(spanned.span),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
check_defined!("Keymap", keymaps, defined_keymaps);
|
|
||||||
check_defined!("DRM device", drm_devices, defined_drm_devices);
|
|
||||||
check_defined!("Output", outputs, defined_outputs);
|
|
||||||
check_defined!("Input", inputs, defined_inputs);
|
|
||||||
Some(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn default_config_parses() {
|
|
||||||
let input = include_bytes!("default-config.toml");
|
|
||||||
parse_config(input, &Default::default(), |_| ()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn custom_animation_curve_parses() {
|
|
||||||
let input = b"
|
|
||||||
[animations]
|
|
||||||
curve = [0.25, 0.1, 0.25, 1.0]
|
|
||||||
";
|
|
||||||
let config = parse_config(input, &Default::default(), |_| ()).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
config.animations.curve,
|
|
||||||
Some(AnimationCurveConfig::CubicBezier([0.25, 0.1, 0.25, 1.0]))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn animation_style_parses() {
|
|
||||||
let input = b"
|
|
||||||
[animations]
|
|
||||||
style = \"plain\"
|
|
||||||
";
|
|
||||||
let config = parse_config(input, &Default::default(), |_| ()).unwrap();
|
|
||||||
assert_eq!(config.animations.style.as_deref(), Some("plain"));
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
pub use jay_toml::value_parser::{
|
|
||||||
DataType, ParseResult, Parser, UnexpectedDataType,
|
|
||||||
};
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
config::{
|
|
||||||
AnimationCurveConfig, Animations,
|
|
||||||
context::Context,
|
|
||||||
extractor::{Extractor, ExtractorError, bol, n32, opt, recover, str, val},
|
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
|
||||||
},
|
|
||||||
jay_toml::{
|
|
||||||
toml_span::{DespanExt, Span, Spanned, SpannedExt},
|
|
||||||
toml_value::Value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
indexmap::IndexMap,
|
|
||||||
thiserror::Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum AnimationsParserError {
|
|
||||||
#[error(transparent)]
|
|
||||||
Expected(#[from] UnexpectedDataType),
|
|
||||||
#[error(transparent)]
|
|
||||||
Extract(#[from] ExtractorError),
|
|
||||||
#[error("Expected animation curve to be a string or an array")]
|
|
||||||
CurveType,
|
|
||||||
#[error("Cubic-bezier animation curves must contain exactly four values")]
|
|
||||||
CubicBezierLen,
|
|
||||||
#[error("Cubic-bezier animation curve entries must be finite floats or integers")]
|
|
||||||
CubicBezierValue,
|
|
||||||
#[error("Cubic-bezier x control points must be between 0 and 1")]
|
|
||||||
CubicBezierXRange,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AnimationsParser<'a>(pub &'a Context<'a>);
|
|
||||||
|
|
||||||
impl Parser for AnimationsParser<'_> {
|
|
||||||
type Value = Animations;
|
|
||||||
type Error = AnimationsParserError;
|
|
||||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
|
||||||
|
|
||||||
fn parse_table(
|
|
||||||
&mut self,
|
|
||||||
span: Span,
|
|
||||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
|
||||||
) -> ParseResult<Self> {
|
|
||||||
let mut ext = Extractor::new(self.0, span, table);
|
|
||||||
let (enabled, duration_ms, style, curve) = ext.extract((
|
|
||||||
recover(opt(bol("enabled"))),
|
|
||||||
recover(opt(n32("duration-ms"))),
|
|
||||||
recover(opt(str("style"))),
|
|
||||||
opt(val("curve")),
|
|
||||||
))?;
|
|
||||||
let curve = match curve {
|
|
||||||
Some(curve) => Some(parse_curve(curve)?),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
Ok(Animations {
|
|
||||||
enabled: enabled.despan(),
|
|
||||||
duration_ms: duration_ms.despan(),
|
|
||||||
style: style.despan().map(|style| style.to_string()),
|
|
||||||
curve,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_curve(
|
|
||||||
curve: Spanned<&Value>,
|
|
||||||
) -> Result<AnimationCurveConfig, Spanned<AnimationsParserError>> {
|
|
||||||
match curve.value {
|
|
||||||
Value::String(s) => Ok(AnimationCurveConfig::Preset(s.clone())),
|
|
||||||
Value::Array(values) => parse_cubic_bezier(curve.span, values),
|
|
||||||
_ => Err(AnimationsParserError::CurveType.spanned(curve.span)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_cubic_bezier(
|
|
||||||
span: Span,
|
|
||||||
values: &[Spanned<Value>],
|
|
||||||
) -> Result<AnimationCurveConfig, Spanned<AnimationsParserError>> {
|
|
||||||
if values.len() != 4 {
|
|
||||||
return Err(AnimationsParserError::CubicBezierLen.spanned(span));
|
|
||||||
}
|
|
||||||
let mut points = [0.0; 4];
|
|
||||||
for (idx, value) in values.iter().enumerate() {
|
|
||||||
let f = match value.value {
|
|
||||||
Value::Float(f) => f,
|
|
||||||
Value::Integer(i) => i as f64,
|
|
||||||
_ => return Err(AnimationsParserError::CubicBezierValue.spanned(value.span)),
|
|
||||||
};
|
|
||||||
if !f.is_finite() {
|
|
||||||
return Err(AnimationsParserError::CubicBezierValue.spanned(value.span));
|
|
||||||
}
|
|
||||||
points[idx] = f as f32;
|
|
||||||
}
|
|
||||||
if !(0.0..=1.0).contains(&points[0]) || !(0.0..=1.0).contains(&points[2]) {
|
|
||||||
return Err(AnimationsParserError::CubicBezierXRange.spanned(span));
|
|
||||||
}
|
|
||||||
Ok(AnimationCurveConfig::CubicBezier(points))
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
config::{
|
|
||||||
Scratchpad,
|
|
||||||
context::Context,
|
|
||||||
extractor::{Extractor, ExtractorError, opt, str, val},
|
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
|
||||||
parsers::exec::{ExecParser, ExecParserError},
|
|
||||||
},
|
|
||||||
jay_toml::{
|
|
||||||
toml_span::{Span, Spanned},
|
|
||||||
toml_value::Value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
indexmap::IndexMap,
|
|
||||||
thiserror::Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum ScratchpadParserError {
|
|
||||||
#[error(transparent)]
|
|
||||||
Expected(#[from] UnexpectedDataType),
|
|
||||||
#[error(transparent)]
|
|
||||||
Extract(#[from] ExtractorError),
|
|
||||||
#[error(transparent)]
|
|
||||||
Exec(#[from] ExecParserError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ScratchpadParser<'a>(pub &'a Context<'a>);
|
|
||||||
|
|
||||||
impl Parser for ScratchpadParser<'_> {
|
|
||||||
type Value = Scratchpad;
|
|
||||||
type Error = ScratchpadParserError;
|
|
||||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
|
||||||
|
|
||||||
fn parse_table(
|
|
||||||
&mut self,
|
|
||||||
span: Span,
|
|
||||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
|
||||||
) -> ParseResult<Self> {
|
|
||||||
let mut ext = Extractor::new(self.0, span, table);
|
|
||||||
let (name, exec_val) = ext.extract((str("name"), opt(val("exec"))))?;
|
|
||||||
let exec = match exec_val {
|
|
||||||
None => None,
|
|
||||||
Some(e) => Some(e.parse_map(&mut ExecParser(self.0))?),
|
|
||||||
};
|
|
||||||
Ok(Scratchpad {
|
|
||||||
name: name.value.to_string(),
|
|
||||||
exec,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ScratchpadsParser<'a>(pub &'a Context<'a>);
|
|
||||||
|
|
||||||
impl Parser for ScratchpadsParser<'_> {
|
|
||||||
type Value = Vec<Scratchpad>;
|
|
||||||
type Error = ScratchpadParserError;
|
|
||||||
const EXPECTED: &'static [DataType] = &[DataType::Table, DataType::Array];
|
|
||||||
|
|
||||||
fn parse_array(&mut self, _span: Span, array: &[Spanned<Value>]) -> ParseResult<Self> {
|
|
||||||
let mut res = vec![];
|
|
||||||
for el in array {
|
|
||||||
match el.parse(&mut ScratchpadParser(self.0)) {
|
|
||||||
Ok(o) => res.push(o),
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Could not parse scratchpad: {}", self.0.error(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_table(
|
|
||||||
&mut self,
|
|
||||||
span: Span,
|
|
||||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
|
||||||
) -> ParseResult<Self> {
|
|
||||||
log::warn!(
|
|
||||||
"`scratchpads` value should be an array: {}",
|
|
||||||
self.0.error3(span)
|
|
||||||
);
|
|
||||||
ScratchpadParser(self.0)
|
|
||||||
.parse_table(span, table)
|
|
||||||
.map(|v| vec![v])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
pub use jay_toml::SpannedErrorExt;
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-toml"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
description = "Internal TOML parser used by Jay"
|
|
||||||
repository = "https://github.com/mahkoh/jay"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bstr = { version = "1.9.1", default-features = false }
|
|
||||||
indexmap = "2.2.5"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
serde_json = "1.0.114"
|
|
||||||
walkdir = "2.5.0"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-tracy"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
ahash = { version = "0.8.7", optional = true }
|
|
||||||
parking_lot = { version = "0.12.1", optional = true }
|
|
||||||
rustc-demangle = { version = "0.1.24", optional = true }
|
|
||||||
tracy-client-sys = { version = "0.24.1", features = ["ondemand", "manual-lifetime", "debuginfod", "demangle"], optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
tracy = ["dep:ahash", "dep:parking_lot", "dep:rustc-demangle", "dep:tracy-client-sys"]
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-tree-types"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-config = { path = "../jay-config" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
|
|
||||||
linearize = { version = "0.1.3", features = ["derive"] }
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
use {
|
|
||||||
jay_config::{
|
|
||||||
Direction as ConfigDirection,
|
|
||||||
video::Transform as ConfigTransform,
|
|
||||||
window::TileState as ConfigTileState,
|
|
||||||
workspace::WorkspaceDisplayOrder as ConfigWorkspaceDisplayOrder,
|
|
||||||
},
|
|
||||||
jay_utils::static_text::StaticText,
|
|
||||||
linearize::{Linearize, LinearizeExt},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default, Linearize)]
|
|
||||||
pub enum WorkspaceDisplayOrder {
|
|
||||||
#[default]
|
|
||||||
Manual,
|
|
||||||
Sorted,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigWorkspaceDisplayOrder> for WorkspaceDisplayOrder {
|
|
||||||
fn from(value: ConfigWorkspaceDisplayOrder) -> Self {
|
|
||||||
match value {
|
|
||||||
ConfigWorkspaceDisplayOrder::Manual => WorkspaceDisplayOrder::Manual,
|
|
||||||
ConfigWorkspaceDisplayOrder::Sorted => WorkspaceDisplayOrder::Sorted,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<ConfigWorkspaceDisplayOrder> for WorkspaceDisplayOrder {
|
|
||||||
fn into(self) -> ConfigWorkspaceDisplayOrder {
|
|
||||||
match self {
|
|
||||||
WorkspaceDisplayOrder::Manual => ConfigWorkspaceDisplayOrder::Manual,
|
|
||||||
WorkspaceDisplayOrder::Sorted => ConfigWorkspaceDisplayOrder::Sorted,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticText for WorkspaceDisplayOrder {
|
|
||||||
fn text(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
WorkspaceDisplayOrder::Manual => "Manual",
|
|
||||||
WorkspaceDisplayOrder::Sorted => "Sorted",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default, Linearize)]
|
|
||||||
pub enum Transform {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Rotate90,
|
|
||||||
Rotate180,
|
|
||||||
Rotate270,
|
|
||||||
Flip,
|
|
||||||
FlipRotate90,
|
|
||||||
FlipRotate180,
|
|
||||||
FlipRotate270,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticText for Transform {
|
|
||||||
fn text(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Transform::None => "none",
|
|
||||||
Transform::Rotate90 => "rotate-90",
|
|
||||||
Transform::Rotate180 => "rotate-180",
|
|
||||||
Transform::Rotate270 => "rotate-270",
|
|
||||||
Transform::Flip => "flip",
|
|
||||||
Transform::FlipRotate90 => "flip-rotate-90",
|
|
||||||
Transform::FlipRotate180 => "flip-rotate-180",
|
|
||||||
Transform::FlipRotate270 => "flip-rotate-270",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigTransform> for Transform {
|
|
||||||
fn from(value: ConfigTransform) -> Self {
|
|
||||||
match value {
|
|
||||||
ConfigTransform::None => Transform::None,
|
|
||||||
ConfigTransform::Rotate90 => Transform::Rotate90,
|
|
||||||
ConfigTransform::Rotate180 => Transform::Rotate180,
|
|
||||||
ConfigTransform::Rotate270 => Transform::Rotate270,
|
|
||||||
ConfigTransform::Flip => Transform::Flip,
|
|
||||||
ConfigTransform::FlipRotate90 => Transform::FlipRotate90,
|
|
||||||
ConfigTransform::FlipRotate180 => Transform::FlipRotate180,
|
|
||||||
ConfigTransform::FlipRotate270 => Transform::FlipRotate270,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<ConfigTransform> for Transform {
|
|
||||||
fn into(self) -> ConfigTransform {
|
|
||||||
match self {
|
|
||||||
Transform::None => ConfigTransform::None,
|
|
||||||
Transform::Rotate90 => ConfigTransform::Rotate90,
|
|
||||||
Transform::Rotate180 => ConfigTransform::Rotate180,
|
|
||||||
Transform::Rotate270 => ConfigTransform::Rotate270,
|
|
||||||
Transform::Flip => ConfigTransform::Flip,
|
|
||||||
Transform::FlipRotate90 => ConfigTransform::FlipRotate90,
|
|
||||||
Transform::FlipRotate180 => ConfigTransform::FlipRotate180,
|
|
||||||
Transform::FlipRotate270 => ConfigTransform::FlipRotate270,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const TF_NORMAL: i32 = 0;
|
|
||||||
const TF_90: i32 = 1;
|
|
||||||
const TF_180: i32 = 2;
|
|
||||||
const TF_270: i32 = 3;
|
|
||||||
const TF_FLIPPED: i32 = 4;
|
|
||||||
const TF_FLIPPED_90: i32 = 5;
|
|
||||||
const TF_FLIPPED_180: i32 = 6;
|
|
||||||
const TF_FLIPPED_270: i32 = 7;
|
|
||||||
|
|
||||||
impl Transform {
|
|
||||||
pub fn maybe_swap<T>(self, (left, right): (T, T)) -> (T, T) {
|
|
||||||
match self {
|
|
||||||
Self::None | Self::Rotate180 | Self::Flip | Self::FlipRotate180 => (left, right),
|
|
||||||
Self::Rotate90 | Self::Rotate270 | Self::FlipRotate90 | Self::FlipRotate270 => {
|
|
||||||
(right, left)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_wl(self) -> i32 {
|
|
||||||
match self {
|
|
||||||
Self::None => TF_NORMAL,
|
|
||||||
Self::Rotate90 => TF_90,
|
|
||||||
Self::Rotate180 => TF_180,
|
|
||||||
Self::Rotate270 => TF_270,
|
|
||||||
Self::Flip => TF_FLIPPED,
|
|
||||||
Self::FlipRotate90 => TF_FLIPPED_90,
|
|
||||||
Self::FlipRotate180 => TF_FLIPPED_180,
|
|
||||||
Self::FlipRotate270 => TF_FLIPPED_270,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_wl(wl: i32) -> Option<Self> {
|
|
||||||
let tf = match wl {
|
|
||||||
TF_NORMAL => Self::None,
|
|
||||||
TF_90 => Self::Rotate90,
|
|
||||||
TF_180 => Self::Rotate180,
|
|
||||||
TF_270 => Self::Rotate270,
|
|
||||||
TF_FLIPPED => Self::Flip,
|
|
||||||
TF_FLIPPED_90 => Self::FlipRotate90,
|
|
||||||
TF_FLIPPED_180 => Self::FlipRotate180,
|
|
||||||
TF_FLIPPED_270 => Self::FlipRotate270,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
Some(tf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_point(self, width: i32, height: i32, (x, y): (i32, i32)) -> (i32, i32) {
|
|
||||||
match self {
|
|
||||||
Self::None => (x, y),
|
|
||||||
Self::Rotate90 => (y, height - x),
|
|
||||||
Self::Rotate180 => (width - x, height - y),
|
|
||||||
Self::Rotate270 => (width - y, x),
|
|
||||||
Self::Flip => (width - x, y),
|
|
||||||
Self::FlipRotate90 => (y, x),
|
|
||||||
Self::FlipRotate180 => (x, height - y),
|
|
||||||
Self::FlipRotate270 => (width - y, height - x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inverse(self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Rotate90 => Self::Rotate270,
|
|
||||||
Self::Rotate270 => Self::Rotate90,
|
|
||||||
_ => self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Linearize)]
|
|
||||||
pub enum TileState {
|
|
||||||
Tiled,
|
|
||||||
Floating,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ConfigTileState> for TileState {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: ConfigTileState) -> Result<Self, Self::Error> {
|
|
||||||
let v = match value {
|
|
||||||
ConfigTileState::Tiled => TileState::Tiled,
|
|
||||||
ConfigTileState::Floating => TileState::Floating,
|
|
||||||
_ => return Err(()),
|
|
||||||
};
|
|
||||||
Ok(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<ConfigTileState> for TileState {
|
|
||||||
fn into(self) -> ConfigTileState {
|
|
||||||
match self {
|
|
||||||
TileState::Tiled => ConfigTileState::Tiled,
|
|
||||||
TileState::Floating => ConfigTileState::Floating,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum Direction {
|
|
||||||
Unspecified,
|
|
||||||
Left,
|
|
||||||
Down,
|
|
||||||
Up,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigDirection> for Direction {
|
|
||||||
fn from(d: ConfigDirection) -> Self {
|
|
||||||
match d {
|
|
||||||
ConfigDirection::Left => Self::Left,
|
|
||||||
ConfigDirection::Down => Self::Down,
|
|
||||||
ConfigDirection::Up => Self::Up,
|
|
||||||
ConfigDirection::Right => Self::Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub enum FindTreeResult {
|
|
||||||
AcceptsInput,
|
|
||||||
Other,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FindTreeResult {
|
|
||||||
pub fn accepts_input(self) -> bool {
|
|
||||||
self == Self::AcceptsInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum FindTreeUsecase {
|
|
||||||
None,
|
|
||||||
SelectToplevel,
|
|
||||||
SelectToplevelOrPopup,
|
|
||||||
SelectWorkspace,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Linearize, Eq, PartialEq, Debug)]
|
|
||||||
pub enum NodeLayer {
|
|
||||||
Display,
|
|
||||||
Layer0,
|
|
||||||
Layer1,
|
|
||||||
Output,
|
|
||||||
Workspace,
|
|
||||||
Tiled,
|
|
||||||
Fullscreen,
|
|
||||||
Stacked,
|
|
||||||
Layer2,
|
|
||||||
Layer3,
|
|
||||||
StackedAboveLayers,
|
|
||||||
Lock,
|
|
||||||
InputMethod,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeLayer {
|
|
||||||
pub fn prev(self) -> Self {
|
|
||||||
if self == NodeLayer::Display {
|
|
||||||
return NodeLayer::InputMethod;
|
|
||||||
}
|
|
||||||
Self::from_linear(self.linearize() - 1).unwrap_or(NodeLayer::InputMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(self) -> Self {
|
|
||||||
Self::from_linear(self.linearize() + 1).unwrap_or(NodeLayer::Display)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "jay-udmabuf"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jay-allocator = { path = "../allocator" }
|
|
||||||
jay-formats = { path = "../formats" }
|
|
||||||
jay-utils = { path = "../utils" }
|
|
||||||
jay-video-types = { path = "../video-types" }
|
|
||||||
|
|
||||||
log = "0.4.20"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
uapi = "0.2.13"
|
|
||||||
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