1
0
Fork 0
forked from wry/wry

Compare commits

..

2 commits
master ... dpms

767 changed files with 21083 additions and 30533 deletions

4
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "crates/toml-config/toml-test"]
path = crates/toml-config/toml-test
[submodule "toml-config/toml-test"]
path = toml-config/toml-test
url = https://github.com/mahkoh/toml-tests.git

499
Cargo.lock generated
View file

@ -625,21 +625,11 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "jay-algorithms"
version = "1.12.0"
version = "0.4.0"
dependencies = [
"smallvec",
]
[[package]]
name = "jay-allocator"
version = "1.12.0"
dependencies = [
"jay-formats",
"jay-video-types",
"thiserror",
"uapi",
]
[[package]]
name = "jay-ash"
version = "0.3.0+1.4.344"
@ -649,52 +639,6 @@ dependencies = [
"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]]
name = "jay-compositor"
version = "1.12.0"
@ -720,48 +664,9 @@ dependencies = [
"indexmap",
"isnt 0.2.0",
"jay-algorithms",
"jay-allocator",
"jay-ash",
"jay-async-engine",
"jay-bufio",
"jay-bugs",
"jay-clientmem",
"jay-cmm",
"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-tracy",
"jay-tree-types",
"jay-udmabuf",
"jay-units",
"jay-utils",
"jay-video-types",
"jay-wheel",
"jay-wire-buf",
"jay-wire-types",
"jay-xcon",
"kbvm",
"libloading",
"linearize",
@ -777,11 +682,13 @@ dependencies = [
"regex",
"repc",
"run-on-drop",
"rustc-demangle",
"serde",
"serde_json",
"smallvec",
"thiserror",
"tiny-skia",
"tracy-client-sys",
"uapi",
"walkdir",
"with_builtin_macros",
@ -789,9 +696,10 @@ dependencies = [
[[package]]
name = "jay-config"
version = "1.12.0"
version = "1.10.0"
dependencies = [
"backtrace",
"bincode",
"bstr",
"error_reporter",
"futures-util",
@ -803,409 +711,24 @@ dependencies = [
"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]]
name = "jay-toml-config"
version = "1.12.0"
version = "0.12.0"
dependencies = [
"ahash",
"bstr",
"error_reporter",
"indexmap",
"jay-config",
"jay-config-schema",
"jay-toml",
"kbvm",
"log",
"phf",
"run-on-drop",
"serde_json",
"simplelog",
"thiserror",
"uapi",
]
[[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",
"walkdir",
]
[[package]]
@ -1999,7 +1522,7 @@ dependencies = [
[[package]]
name = "toml-spec"
version = "1.12.0"
version = "0.1.0"
dependencies = [
"anyhow",
"error_reporter",
@ -2411,7 +1934,7 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "wire-to-xml"
version = "1.12.0"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
@ -2528,7 +2051,7 @@ dependencies = [
[[package]]
name = "xml-to-wire"
version = "1.12.0"
version = "0.1.0"
dependencies = [
"quick-xml",
"thiserror",

View file

@ -1,9 +1,9 @@
[package]
name = "jay-compositor"
version.workspace = true
edition.workspace = true
version = "1.12.0"
edition = "2024"
build = "build/build.rs"
license.workspace = true
license = "GPL-3.0-only"
description = "The Jay compositor"
repository = "https://github.com/mahkoh/jay"
default-run = "jay"
@ -13,61 +13,7 @@ name = "jay"
path = "src/main.rs"
[workspace]
resolver = "3"
members = [
"crates/jay-config",
"crates/jay-config-schema",
"crates/geometry",
"crates/layout-animation",
"crates/formats",
"crates/edid",
"crates/units",
"crates/utils",
"crates/criteria",
"crates/cmm",
"crates/time",
"crates/tracy",
"crates/async-engine",
"crates/io-uring",
"crates/bufio",
"crates/dbus-core",
"crates/xcon",
"crates/wire-types",
"crates/wire-buf",
"crates/tree-types",
"crates/eventfd-cache",
"crates/wheel",
"crates/cpu-worker",
"crates/sighand",
"crates/pr-caps",
"crates/bugs",
"crates/logger",
"crates/video-types",
"crates/output-types",
"crates/input-types",
"crates/keyboard",
"crates/gfx-types",
"crates/theme",
"crates/clientmem",
"crates/allocator",
"crates/output-schedule",
"crates/drm-feedback",
"crates/udmabuf",
"crates/damage",
"crates/pango",
"crates/libinput",
"crates/toml-config",
"crates/toml-parser",
"crates/algorithms",
"crates/toml-spec",
"crates/wire-to-xml",
"crates/xml-to-wire",
]
[workspace.package]
version = "1.12.0"
edition = "2024"
license = "GPL-3.0-only"
members = ["jay-config", "toml-config", "algorithms", "toml-spec", "wire-to-xml", "xml-to-wire"]
[profile.release]
panic = "abort"
@ -77,48 +23,9 @@ debug = "full"
panic = "abort"
[dependencies]
jay-config = { path = "crates/jay-config" }
jay-toml-config = { path = "crates/toml-config" }
jay-algorithms = { path = "crates/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" }
jay-config = { version = "1.10.0", path = "jay-config" }
jay-toml-config = { version = "0.12.0", path = "toml-config" }
jay-algorithms = { version = "0.4.0", path = "algorithms" }
uapi = "0.2.13"
thiserror = "2.0.11"
@ -151,6 +58,8 @@ serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.128"
linearize = { version = "0.1.3", features = ["derive"] }
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"] }
tiny-skia = { version = "0.12.0", default-features = false, features = ["std"] }
regex = "1.11.1"
@ -181,5 +90,5 @@ opt-level = 3
[features]
rc_tracking = []
it = ["jay-async-engine/it", "jay-cpu-worker/it"]
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy", "jay-clientmem/tracy"]
it = []
tracy = ["dep:tracy-client-sys", "dep:rustc-demangle"]

View file

@ -3,7 +3,7 @@
[![crates.io](https://img.shields.io/crates/v/jay-compositor.svg)](http://crates.io/crates/jay-compositor)
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.
![screenshot.png](static/screenshot.png)

View file

@ -1,8 +1,8 @@
[package]
name = "jay-algorithms"
version.workspace = true
edition.workspace = true
license.workspace = true
version = "0.4.0"
edition = "2024"
license = "GPL-3.0-only"
description = "Internal dependency of the Jay compositor"
repository = "https://github.com/mahkoh/jay"

View file

@ -27,14 +27,14 @@ The table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
| File | What it tells you |
|------|-------------------|
| `crates/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) |
| `crates/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-spec/spec/spec.yaml` | **Canonical** TOML config spec: every key, action, match criterion, type |
| `toml-config/src/default-config.toml` | Built-in default config (keybindings, startup actions) |
| `toml-config/src/config/parsers/action.rs` | Action parser — see which `type` strings are accepted |
| `toml-config/src/lib.rs` | Action dispatch — `window_or_seat!` macro shows which actions work in window rules |
| `src/config/handler.rs` | Config handler; `update_capabilities` shows capability replacement semantics |
| `src/cli/*.rs` | CLI subcommands (clap definitions) |
| `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
@ -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.
- **TOML formatting:** multiline with trailing commas, 4-space indent.
- **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.
- **Control center docs:** verify field names, ordering, and conditional
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
1. Read `git diff` for the commit introducing the action. Key files:
- `crates/toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
- `crates/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)
- `crates/jay-config/src/input.rs` and/or `crates/jay-config/src/window.rs` — Rust API
- `toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
- `toml-config/src/config/parsers/action.rs` — parser (field names, types, defaults)
- `toml-config/src/lib.rs` — dispatch (check if `window_or_seat!` is used)
- `jay-config/src/input.rs` and/or `jay-config/src/window.rs` — Rust API
2. Edit `book/src/configuration/shortcuts.md`:
- **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
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).
3. Add the field with a definition-list entry or example, matching the
existing style of that chapter.

View file

@ -35,6 +35,7 @@
- [Mouse Interactions](mouse.md)
- [Input Modes](input-modes.md)
- [Window & Client Rules](window-rules.md)
- [Screen Sharing](screen-sharing.md)
- [HDR & Color Management](hdr.md)
# Reference

View file

@ -674,6 +674,18 @@ Show color management status:
## 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`
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 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`
Generate shell completion scripts:

View file

@ -62,10 +62,16 @@ on-idle = {
type = "exec",
exec = {
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:
```toml
@ -74,6 +80,7 @@ on-idle = [
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
},
{ type = "exec", exec = ["notify-send", "System locked"] },
@ -93,6 +100,7 @@ on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```

View file

@ -30,8 +30,9 @@ the color management protocol.
## Libei
[libei](https://gitlab.freedesktop.org/libinput/libei) allows applications to
emulate input events. Setting `enable-socket` exposes an unauthenticated socket
that any application can use.
emulate input events. By default, applications can only access libei through
the portal (which prompts the user for permission). Setting `enable-socket`
exposes an unauthenticated socket that any application can use without a prompt.
```toml
libei.enable-socket = false # default

View file

@ -145,6 +145,7 @@ alt-shift-r = "reload-config-toml"
(the next pressed key identifies the mark). See [Marks](#marks) below.
- `enable-window-management`, `disable-window-management` -- programmatically
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
actions.
@ -308,6 +309,7 @@ alt-s = {
type = "exec",
exec = {
shell = "grim - | wl-copy",
privileged = true,
},
}
```
@ -326,6 +328,12 @@ Table fields:
`env`
: 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
Volume control with `pactl`:
@ -354,6 +362,7 @@ Print = {
type = "exec",
exec = {
shell = "grim - | wl-copy",
privileged = true,
},
}
```

View file

@ -63,10 +63,15 @@ on-idle = {
type = "exec",
exec = {
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
configured separately in the `[idle]` section (see [Idle & Screen
Locking](idle.md)):
@ -78,6 +83,7 @@ on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
@ -91,6 +97,7 @@ on-idle = [
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
},
]

View file

@ -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
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

View file

@ -87,5 +87,5 @@ Xwayland client itself:
```toml
[[clients]]
match.is-xwayland = true
# ... configure client-specific behavior
# ... grant capabilities, etc.
```

View file

@ -61,7 +61,10 @@ Commands:
unlock Unlocks the compositor
screenshot Take a screenshot
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
portal Run the desktop portal
randr Inspect/modify graphics card and connector settings
input Inspect/modify input settings
xwayland Inspect/modify xwayland settings
@ -98,6 +101,17 @@ runtime.
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
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
`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
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_pointer_warp_v1 | 1 | |
| wp_presentation | 2 | |
| wp_security_context_manager_v1 | 1 | |
| wp_single_pixel_buffer_manager_v1 | 1 | |
| wp_tearing_control_manager_v1 | 1 | |
| wp_viewporter | 1 | |

View file

@ -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).
- **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.
## Building
@ -128,7 +129,14 @@ retains `CAP_SYS_NICE` solely for creating elevated Vulkan queues later.
> [!NOTE]
> 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`:
@ -136,7 +144,11 @@ You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
~$ 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
@ -144,6 +156,7 @@ The following applications work well with Jay:
- **[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.
- **[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.
- **[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`).

View file

@ -22,11 +22,12 @@
Jay is a Wayland compositor for Linux with an i3-inspired tiling layout. It
supports Vulkan and OpenGL rendering, multi-GPU setups, fractional scaling,
variable refresh rate (VRR), tearing presentation, and HDR. X11 applications
are supported through Xwayland.
variable refresh rate (VRR), tearing presentation, HDR, and screen sharing via
xdg-desktop-portal. X11 applications are supported through Xwayland.
Jay is configured through a declarative TOML file. A comprehensive command-line
interface makes scripting and automation straightforward.
Jay is configured through a declarative TOML file, with an optional advanced
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
Jay can do, or jump straight to [Installation](installation.md) to get started.

View file

@ -84,8 +84,8 @@ This is especially useful for:
## Other
**Toplevel selection.** Some actions ask you to select a window, indicated by a
purple overlay. During this selection, right-click a
**Toplevel selection.** Some actions (like screen sharing) ask you to select 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.
**Canceling interactions.** Press `Escape` to cancel any in-progress mouse

111
book/src/screen-sharing.md Normal file
View 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`

View file

@ -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
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
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire

View file

@ -54,6 +54,57 @@ bindings.
> when any config file exists. Always use `jay config init` to start with a
> 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
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
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
Jay uses Xwayland to run X11 applications.

View file

@ -31,6 +31,12 @@ Each client rule can have the following fields:
`latch`
: 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
All client match criteria are constant over the lifetime of a client. If no
@ -64,6 +70,142 @@ implicitly AND-combined.
`exe` / `exe-regex`
: 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 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
```toml

View file

@ -123,15 +123,16 @@ laptop.
## Workspace Capture
By default, newly created workspaces can be captured by capture clients. You can
disable this globally:
By default, newly created workspaces can be captured for screen sharing. You
can disable this globally:
```toml
workspace-capture = false
```
When workspace capture is enabled, compositor-native capture clients may capture
individual workspaces instead of whole outputs.
When workspace capture is enabled, screen-sharing applications can share
individual workspaces (in addition to full outputs and individual windows). See
[Screen Sharing](screen-sharing.md) for more details.
## Matching Windows by Workspace

View file

@ -4,27 +4,18 @@ use {
std::{env, io::Write},
};
#[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);
#[expect(unused_macros)]
#[macro_use]
#[path = "../src/macros.rs"]
mod macros;
impl $name {
pub fn raw(self) -> i32 {
self.0
}
}
#[path = "../src/libinput/consts.rs"]
mod libinput;
pub const $uc: &[i32] = &[$($val,)*];
#[path = "../src/pango/consts.rs"]
mod pango;
$(
pub const $name2: $name = $name($val);
)*
}
}
#[path = "fontconfig_consts.rs"]
#[path = "../src/fontconfig/consts.rs"]
mod fontconfig;
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<()> {
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")?;
write_ty(&mut f, fontconfig::FC_MATCH_KINDS, "FcMatchKind")?;
write_ty(&mut f, fontconfig::FC_RESULTS, "FcResult")?;

View file

@ -4,10 +4,17 @@ use {
};
pub fn main() -> anyhow::Result<()> {
create_bridge()?;
create_version()?;
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<()> {
let mut version_string = env!("CARGO_PKG_VERSION").to_string();
if let Ok(output) = Command::new("git").arg("rev-parse").arg("HEAD").output()

View file

@ -417,7 +417,6 @@ fn write_file<W: Write>(
let messages = parse_messages(&contents)?;
writeln!(f)?;
writeln!(f, "pub mod {} {{", obj_name)?;
writeln!(f, " #![allow(dead_code)]")?;
writeln!(f, " use super::*;")?;
for message in messages.requests.iter().chain(messages.events.iter()) {
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 uapi::OwnedFd;")?;
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::object::{{ObjectId, Interface}};")?;
writeln!(

View file

@ -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"

View file

@ -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;
}

View file

@ -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"]

View file

@ -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"

View file

@ -1,8 +0,0 @@
[package]
name = "jay-bugs"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
ahash = "0.8.7"

View file

@ -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"]

View file

@ -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())
}
}

View file

@ -1,8 +0,0 @@
[package]
name = "jay-cmm"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
jay-utils = { path = "../utils" }

View file

@ -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;

View file

@ -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"]

View file

@ -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"

View file

@ -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 {}

View file

@ -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" }

View file

@ -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 _,
}
}
}

View file

@ -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"

View file

@ -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,
};
}

View file

@ -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"

View file

@ -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,
}

View file

@ -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"

View file

@ -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"

View file

@ -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" }

View file

@ -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,
];

View file

@ -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"] }

View file

@ -1,8 +0,0 @@
[package]
name = "jay-gfx-types"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
uapi = "0.2.13"

View file

@ -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(())
}
}

View file

@ -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"] }

View file

@ -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,
},
}

View file

@ -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"

View file

@ -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" }

View file

@ -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,
}

View file

@ -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]),
}

View file

@ -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>,
}

View file

@ -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>,
},
}

View file

@ -1,8 +0,0 @@
use jay_config::keyboard::Keymap;
#[derive(Debug, Clone)]
pub enum ConfigKeymap {
Named(String),
Literal(Keymap),
Defined { name: String, map: Keymap },
}

View file

@ -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;

View file

@ -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>,
}

View file

@ -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>,
}

View file

@ -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>,
}

View file

@ -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>,
}

View file

@ -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>,
}

View file

@ -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,
};
}

View file

@ -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"

View file

@ -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());
}
_ => {}
}
}
}
}

View file

@ -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

View file

@ -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"

View file

@ -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(())
}

View file

@ -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()
);
}

View file

@ -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"

View file

@ -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"

View file

@ -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))
}

View file

@ -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"

View file

@ -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()),
})
}
}

View file

@ -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"

View file

@ -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(())
}

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -1,8 +0,0 @@
[package]
name = "jay-time"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
uapi = "0.2.13"

View file

@ -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"));
}

View file

@ -1,3 +0,0 @@
pub use jay_toml::value_parser::{
DataType, ParseResult, Parser, UnexpectedDataType,
};

View file

@ -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))
}

View file

@ -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])
}
}

View file

@ -1 +0,0 @@
pub use jay_toml::SpannedErrorExt;

View file

@ -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"

View file

@ -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"]

View file

@ -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"] }

View file

@ -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)
}
}

View file

@ -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