1
0
Fork 0
forked from wry/wry

Compare commits

..

8 commits
dpms ... master

767 changed files with 30529 additions and 21079 deletions

4
.gitmodules vendored
View file

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

499
Cargo.lock generated
View file

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

View file

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

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, screen sharing, and more.
Vulkan and OpenGL rendering, multi-GPU support, and more.
![screenshot.png](static/screenshot.png)

View file

@ -27,14 +27,14 @@ The table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
| File | What it tells you |
|------|-------------------|
| `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 |
| `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 |
| `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) |
| `toml-config/src/config/parsers/exec.rs` | Exec parser (string, array, or table forms) |
| `crates/toml-config/src/config/parsers/exec.rs` | Exec parser (string, array, or table forms) |
### Known spec.yaml bugs
@ -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/toml-spec/spec/spec.generated.md)
[spec.generated.md](https://github.com/mahkoh/jay/blob/master/crates/toml-spec/spec/spec.generated.md)
for exhaustive listings.
- **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:
- `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
- `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
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 `toml-spec/spec/spec.yaml` for the field definition.
1. Read `crates/toml-spec/spec/spec.yaml` for the field definition.
2. Identify which book chapter covers that config section (see table above).
3. Add the field with a definition-list entry or example, matching the
existing style of that chapter.

View file

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

View file

@ -30,9 +30,8 @@ the color management protocol.
## Libei
[libei](https://gitlab.freedesktop.org/libinput/libei) allows applications to
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.
emulate input events. Setting `enable-socket` exposes an unauthenticated socket
that any application can use.
```toml
libei.enable-socket = false # default

View file

@ -145,7 +145,6 @@ 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.
@ -309,7 +308,6 @@ alt-s = {
type = "exec",
exec = {
shell = "grim - | wl-copy",
privileged = true,
},
}
```
@ -328,12 +326,6 @@ 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`:
@ -362,7 +354,6 @@ Print = {
type = "exec",
exec = {
shell = "grim - | wl-copy",
privileged = true,
},
}
```

View file

@ -63,15 +63,10 @@ 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)):
@ -83,7 +78,6 @@ on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
@ -97,7 +91,6 @@ 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 recorded (e.g. via screen sharing).
when a window is being captured.
### Example

View file

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

View file

@ -61,10 +61,7 @@ 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
@ -101,17 +98,6 @@ 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.
@ -154,20 +140,6 @@ Jay supports running X11 applications seamlessly through Xwayland. See
Jay supports clipboard managers via the `zwlr_data_control_manager_v1` and
`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
@ -252,7 +224,6 @@ 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,7 +63,6 @@ For Vulkan, you also need the driver for your GPU:
- **Linux 6.7 or later** -- required for explicit sync (needed for Nvidia GPUs).
- **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
@ -129,14 +128,7 @@ 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 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`.
### SCHED_RR
You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
@ -144,11 +136,7 @@ You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
~$ JAY_NO_REALTIME=1 jay run
```
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`.
This still allows elevated Vulkan queues.
## Recommended Applications
@ -156,7 +144,6 @@ The following applications work well with Jay:
- **[Alacritty](https://alacritty.org/)** -- the default terminal emulator in the built-in configuration.
- **[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,12 +22,11 @@
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, HDR, and screen sharing via
xdg-desktop-portal. X11 applications are supported through Xwayland.
variable refresh rate (VRR), tearing presentation, and HDR. X11 applications
are supported through Xwayland.
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.
Jay is configured through a declarative TOML file. 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 (like screen sharing) ask you to select a
window, indicated by a purple overlay. During this selection, right-click a
**Toplevel selection.** Some actions 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

View file

@ -1,111 +0,0 @@
# Screen Sharing
Jay supports screen sharing via
[xdg-desktop-portal](https://github.com/flatpak/xdg-desktop-portal). Three
capture types are available:
- **Window capture** -- share a single window.
- **Output capture** -- share an entire monitor.
- **Workspace capture** -- like output capture, but only a single workspace is
shown.
## Requirements
[PipeWire](https://pipewire.org/) must be installed and running. Verify with:
```shell
~$ systemctl --user status pipewire
```
## Portal Setup
Jay implements its own portal backend for the `ScreenCast` and `RemoteDesktop`
interfaces. Two configuration files must be installed so that
`xdg-desktop-portal` knows to use Jay's backend.
### If the Repository is Checked Out
```shell
~$ sudo cp etc/jay.portal /usr/share/xdg-desktop-portal/portals/jay.portal
~$ sudo cp etc/jay-portals.conf /usr/share/xdg-desktop-portal/jay-portals.conf
```
### If Installed via cargo install
Create the files manually:
```shell
~$ sudo tee /usr/share/xdg-desktop-portal/portals/jay.portal > /dev/null << 'EOF'
[portal]
DBusName=org.freedesktop.impl.portal.desktop.jay
Interfaces=org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;
EOF
```
```shell
~$ sudo tee /usr/share/xdg-desktop-portal/jay-portals.conf > /dev/null << 'EOF'
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=jay
org.freedesktop.impl.portal.RemoteDesktop=jay
org.freedesktop.impl.portal.Inhibit=none
org.freedesktop.impl.portal.FileChooser=gtk4
EOF
```
### Restart the Portal
After installing the files, restart the portal service:
```shell
~$ systemctl --user restart xdg-desktop-portal
```
## Configuration
### workspace-capture
The top-level `workspace-capture` setting controls whether newly created
workspaces can be captured via workspace capture. The default is `true`:
```toml
workspace-capture = false
```
Set this to `false` if you want to prevent workspace-level capture by default.
### Capture Indicator Colors
When a window is being recorded, its title bar color changes to make the
capture visually obvious. You can customize these colors in the `[theme]`
table:
```toml
[theme]
captured-focused-title-bg-color = "#900000"
captured-unfocused-title-bg-color = "#5f0000"
```
- `captured-focused-title-bg-color` -- background color of focused title bars
that are being recorded.
- `captured-unfocused-title-bg-color` -- background color of unfocused title
bars that are being recorded.
## The jay portal Command
Jay's portal backend is normally started automatically when a screen-sharing
request comes in via D-Bus activation. If you need to start it manually for
debugging purposes:
```shell
~$ jay portal
```
## Troubleshooting
If screen sharing does not work:
1. Verify PipeWire is running: `systemctl --user status pipewire`
2. Verify the portal files are installed in `/usr/share/xdg-desktop-portal/`.
3. Restart the portal: `systemctl --user restart xdg-desktop-portal`
4. Check the Jay log for errors: `jay log`

View file

@ -77,6 +77,20 @@ 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,57 +54,6 @@ 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:
@ -132,45 +81,6 @@ 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,12 +31,6 @@ 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
@ -70,142 +64,6 @@ 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]]`
@ -456,18 +314,6 @@ action = {
}
```
### Grant Protocol Access to a Trusted App
```toml
[[clients]]
match.comm = "swaylock"
capabilities = ["session-lock", "layer-shell"]
[[clients]]
match.comm = "waybar"
capabilities = ["layer-shell", "foreign-toplevel-list"]
```
### Suppress Focus Stealing for Chromium Screen-Share Windows
```toml

View file

@ -123,16 +123,15 @@ laptop.
## Workspace Capture
By default, newly created workspaces can be captured for screen sharing. You
can disable this globally:
By default, newly created workspaces can be captured by capture clients. You can
disable this globally:
```toml
workspace-capture = false
```
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.
When workspace capture is enabled, compositor-native capture clients may capture
individual workspaces instead of whole outputs.
## Matching Windows by Workspace

View file

@ -4,18 +4,27 @@ use {
std::{env, io::Write},
};
#[expect(unused_macros)]
#[macro_use]
#[path = "../src/macros.rs"]
mod macros;
#[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);
#[path = "../src/libinput/consts.rs"]
mod libinput;
impl $name {
pub fn raw(self) -> i32 {
self.0
}
}
#[path = "../src/pango/consts.rs"]
mod pango;
pub const $uc: &[i32] = &[$($val,)*];
#[path = "../src/fontconfig/consts.rs"]
$(
pub const $name2: $name = $name($val);
)*
}
}
#[path = "fontconfig_consts.rs"]
mod fontconfig;
fn get_target() -> repc::Target {
@ -49,108 +58,6 @@ fn write_ty<W: Write>(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> {
}
pub fn main() -> anyhow::Result<()> {
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,17 +4,10 @@ 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,6 +417,7 @@ 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)?;
@ -442,7 +443,7 @@ pub fn main() -> Result<()> {
writeln!(f, "use std::rc::Rc;")?;
writeln!(f, "use uapi::OwnedFd;")?;
writeln!(f, "use bstr::BStr;")?;
writeln!(f, "use crate::fixed::Fixed;")?;
writeln!(f, "use jay_units::fixed::Fixed;")?;
writeln!(f, "use crate::client::{{EventFormatter, RequestParser}};")?;
writeln!(f, "use crate::object::{{ObjectId, Interface}};")?;
writeln!(

View file

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

View file

@ -0,0 +1,12 @@
[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

@ -0,0 +1,95 @@
use {
jay_formats::Format,
jay_video_types::{
Modifier,
dmabuf::{DmaBuf, DmaBufIds},
},
std::{
error::Error,
ops::{BitOr, BitOrAssign, Not},
rc::Rc,
},
thiserror::Error,
uapi::{OwnedFd, c},
};
#[derive(Debug, Error)]
#[error(transparent)]
pub struct AllocatorError(#[from] pub Box<dyn Error + Send>);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct BufferUsage(u32);
impl BufferUsage {
pub fn none() -> Self {
Self(0)
}
pub fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
}
impl BitOr for BufferUsage {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for BufferUsage {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl Not for BufferUsage {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
pub const BO_USE_SCANOUT: BufferUsage = BufferUsage(1 << 0);
pub const BO_USE_CURSOR: BufferUsage = BufferUsage(1 << 1);
pub const BO_USE_RENDERING: BufferUsage = BufferUsage(1 << 2);
pub const BO_USE_WRITE: BufferUsage = BufferUsage(1 << 3);
pub const BO_USE_LINEAR: BufferUsage = BufferUsage(1 << 4);
pub const BO_USE_PROTECTED: BufferUsage = BufferUsage(1 << 5);
pub trait Allocator {
fn drm(&self) -> Option<&dyn AllocatorDrm>;
fn create_bo(
&self,
dma_buf_ids: &DmaBufIds,
width: i32,
height: i32,
format: &'static Format,
modifiers: &[Modifier],
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
fn import_dmabuf(
&self,
dmabuf: &DmaBuf,
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
}
pub trait AllocatorDrm {
fn dev(&self) -> c::dev_t;
fn dup_render_fd(&self) -> Result<Rc<OwnedFd>, AllocatorError>;
}
pub trait BufferObject {
fn dmabuf(&self) -> &DmaBuf;
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
}
pub trait MappedBuffer {
unsafe fn data(&self) -> &[u8];
fn data_ptr(&self) -> *mut u8;
fn stride(&self) -> i32;
}

View file

@ -0,0 +1,14 @@
[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,11 +1,9 @@
use {
crate::{
async_engine::{AsyncEngine, Phase},
tracy::ZoneName,
utils::{
numcell::NumCell,
ptr_ext::{MutPtrExt, PtrExt},
},
crate::{AsyncEngine, Phase},
jay_tracy::ZoneName,
jay_utils::{
numcell::NumCell,
ptr_ext::{MutPtrExt, PtrExt},
},
std::{
cell::{Cell, UnsafeCell},
@ -142,7 +140,7 @@ impl AsyncEngine {
}),
waker: Cell::new(None),
queue: self.clone(),
zone: create_zone_name!("task:{}", name),
zone: jay_tracy::create_zone_name!("task:{}", name),
});
unsafe {
f.schedule_run();
@ -254,7 +252,7 @@ impl<T, F: Future<Output = T>> Task<T, F> {
let mut ctx = Context::from_waker(&waker);
let poll = {
dynamic_zone!(self.zone);
jay_tracy::dynamic_zone!(self.zone);
Pin::new_unchecked(&mut *data.future).poll(&mut ctx)
};
if let Poll::Ready(d) = poll {

View file

@ -1,5 +1,5 @@
use {
crate::async_engine::AsyncEngine,
crate::AsyncEngine,
std::{
future::Future,
pin::Pin,

View file

@ -1,13 +1,12 @@
mod ae_task;
mod ae_yield;
mod run_toplevel;
pub use {crate::async_engine::ae_yield::Yield, ae_task::SpawnedFuture};
pub use {ae_task::SpawnedFuture, ae_yield::Yield, run_toplevel::*};
use {
crate::{
async_engine::ae_task::Runnable,
time::Time,
utils::{array, numcell::NumCell, syncqueue::SyncQueue},
},
crate::ae_task::Runnable,
jay_time::Time,
jay_utils::{array, numcell::NumCell, syncqueue::SyncQueue},
std::{
cell::{Cell, RefCell},
collections::VecDeque,

View file

@ -1,8 +1,6 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
utils::queue::AsyncQueue,
},
crate::{AsyncEngine, SpawnedFuture},
jay_utils::queue::AsyncQueue,
std::rc::Rc,
};

12
crates/bufio/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[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,11 +1,9 @@
use {
crate::{
io_uring::{IoUring, IoUringError},
utils::{
buf::{Buf, DynamicBuf},
queue::AsyncQueue,
stack::Stack,
},
jay_io_uring::{IoUring, IoUringError},
jay_utils::{
buf::{Buf, DynamicBuf},
queue::AsyncQueue,
stack::Stack,
},
std::{
collections::VecDeque,

8
crates/bugs/Cargo.toml Normal file
View file

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

View file

@ -0,0 +1,18 @@
[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"]

331
crates/clientmem/src/lib.rs Normal file
View file

@ -0,0 +1,331 @@
use {
jay_cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
jay_gfx_types::{ShmMemory, ShmMemoryBacking},
jay_utils::{
oserror::{OsError, OsErrorExt2},
page_size::page_size,
vec_ext::VecExt,
},
std::{
cell::Cell,
error::Error,
mem::{ManuallyDrop, MaybeUninit},
ops::Deref,
ptr,
rc::Rc,
sync::atomic::{Ordering, compiler_fence},
},
thiserror::Error,
uapi::{
OwnedFd, Pod,
c::{self, raise},
ftruncate,
},
};
#[derive(Copy, Clone, Debug)]
pub struct ClientMemClient<'a> {
pub comm: &'a str,
pub id: u64,
}
#[derive(Debug, Error)]
pub enum ClientMemError {
#[error("Could not install the sigbus handler")]
SigactionFailed(#[source] jay_utils::oserror::OsError),
#[error("A SIGBUS occurred while accessing mapped memory")]
Sigbus,
#[error("mmap failed")]
MmapFailed(#[source] jay_utils::oserror::OsError),
#[error("Length was not a multiple of the data element size")]
InvalidLength,
}
pub struct ClientMem {
fd: ManuallyDrop<Rc<OwnedFd>>,
failed: Cell<bool>,
sigbus_impossible: bool,
data: *const [Cell<u8>],
cpu: Option<Rc<CpuWorker>>,
}
#[derive(Clone)]
pub struct ClientMemOffset {
mem: Rc<ClientMem>,
offset: usize,
data: *const [Cell<u8>],
}
impl ClientMem {
pub fn new(
fd: &Rc<OwnedFd>,
len: usize,
read_only: bool,
client: Option<ClientMemClient<'_>>,
cpu: Option<&Rc<CpuWorker>>,
is_udmabuf: bool,
) -> Result<Self, ClientMemError> {
Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf)
}
pub fn new_private(
fd: &Rc<OwnedFd>,
len: usize,
read_only: bool,
client: Option<ClientMemClient<'_>>,
cpu: Option<&Rc<CpuWorker>>,
) -> Result<Self, ClientMemError> {
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false)
}
fn new2(
fd: &Rc<OwnedFd>,
len: usize,
read_only: bool,
client: Option<ClientMemClient<'_>>,
cpu: Option<&Rc<CpuWorker>>,
flags: c::c_int,
is_udmabuf: bool,
) -> Result<Self, ClientMemError> {
let mut sigbus_impossible = is_udmabuf;
let mut real_size = None;
if !sigbus_impossible
&& let Ok(seals) = uapi::fcntl_get_seals(fd.raw())
&& seals & c::F_SEAL_SHRINK != 0
&& let Ok(stat) = uapi::fstat(fd.raw())
{
real_size = Some(stat.st_size as usize);
sigbus_impossible = stat.st_size as u64 >= len as u64;
}
if !sigbus_impossible && let Some(client) = client {
log::debug!(
"Client {} ({}) has created a shm buffer that might cause SIGBUS",
client.comm,
client.id,
);
}
let len = len.next_multiple_of(page_size());
if let Some(real_size) = real_size
&& real_size < len
{
let _ = ftruncate(fd.raw(), len as _);
}
let data = if len == 0 {
&mut [][..]
} else {
let prot = match read_only {
true => c::PROT_READ,
false => c::PROT_READ | c::PROT_WRITE,
};
unsafe {
let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0);
if data == c::MAP_FAILED {
return Err(ClientMemError::MmapFailed(OsError::default()));
}
std::slice::from_raw_parts_mut(data as *mut Cell<u8>, len)
}
};
Ok(Self {
fd: ManuallyDrop::new(fd.clone()),
failed: Cell::new(false),
sigbus_impossible,
data,
cpu: cpu.cloned(),
})
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn offset(self: &Rc<Self>, offset: usize, len: usize) -> ClientMemOffset {
let mem = unsafe { &*self.data };
ClientMemOffset {
mem: self.clone(),
offset,
data: &mem[offset..][..len],
}
}
pub fn fd(&self) -> &Rc<OwnedFd> {
&self.fd
}
pub fn is_sealed_memfd(&self) -> bool {
self.sigbus_impossible
}
}
impl ClientMemOffset {
pub fn pool(&self) -> &ClientMem {
&self.mem
}
pub fn offset(&self) -> usize {
self.offset
}
pub fn ptr(&self) -> *const [Cell<u8>] {
self.data
}
pub fn access<T, F: FnOnce(&[Cell<u8>]) -> T>(&self, f: F) -> Result<T, ClientMemError> {
unsafe {
if self.mem.sigbus_impossible {
return Ok(f(&*self.data));
}
let mref = MemRef {
mem: &*self.mem,
outer: MEM.get(),
};
MEM.set(&mref);
compiler_fence(Ordering::SeqCst);
let res = f(&*self.data);
MEM.set(mref.outer);
compiler_fence(Ordering::SeqCst);
match self.mem.failed.get() {
true => Err(ClientMemError::Sigbus),
_ => Ok(res),
}
}
}
pub fn read<T: Pod>(&self, dst: &mut Vec<T>) -> Result<(), ClientMemError> {
if self.data.len().checked_rem(std::mem::size_of::<T>()) != Some(0) {
return Err(ClientMemError::InvalidLength);
}
self.access(|v| {
let len_elements = v.len() / std::mem::size_of::<T>();
dst.reserve(len_elements);
let (_, unused) = dst.split_at_spare_mut_bytes_ext();
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
unsafe {
dst.set_len(dst.len() + len_elements);
}
})
}
}
impl Drop for ClientMem {
fn drop(&mut self) {
let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
if let Some(cpu) = &self.cpu {
let pending = cpu.submit(Box::new(CloseMemWork {
fd: Rc::try_unwrap(fd).ok(),
data: self.data,
}));
pending.detach();
} else {
unsafe {
c::munmap(self.data as _, self.len());
}
}
}
}
struct MemRef {
mem: *const ClientMem,
outer: *const MemRef,
}
thread_local! {
static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) };
}
unsafe fn kill() -> ! {
unsafe {
c::signal(c::SIGBUS, c::SIG_DFL);
raise(c::SIGBUS);
}
unreachable!();
}
unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) {
unsafe {
assert_eq!(sig, c::SIGBUS);
let mut memr_ptr = MEM.get();
while !memr_ptr.is_null() {
let memr = &*memr_ptr;
let mem = &*memr.mem;
let lo = mem.data as *mut u8 as usize;
let hi = lo + mem.len();
let fault_addr = info.si_addr() as usize;
if fault_addr < lo || fault_addr >= hi {
memr_ptr = memr.outer;
continue;
}
let res = c::mmap64(
lo as _,
hi - lo,
c::PROT_WRITE | c::PROT_READ,
c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED,
-1,
0,
);
if res == c::MAP_FAILED {
kill();
}
mem.failed.set(true);
return;
}
kill();
}
}
pub fn init() -> Result<(), ClientMemError> {
unsafe {
let mut action: c::sigaction = MaybeUninit::zeroed().assume_init();
action.sa_sigaction =
sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _;
action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO;
let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut());
uapi::map_err!(res)
.map(drop)
.map_os_err(ClientMemError::SigactionFailed)
}
}
struct CloseMemWork {
fd: Option<OwnedFd>,
data: *const [Cell<u8>],
}
unsafe impl Send for CloseMemWork {}
impl CpuJob for CloseMemWork {
fn work(&mut self) -> &mut dyn CpuWork {
self
}
fn completed(self: Box<Self>) {
// nothing
}
}
impl CpuWork for CloseMemWork {
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
jay_tracy::zone!("CloseMemWork");
self.fd.take();
unsafe {
c::munmap(self.data as _, self.data.len());
}
None
}
}
impl ShmMemory for ClientMemOffset {
fn len(&self) -> usize {
self.data.len()
}
fn safe_access(&self) -> ShmMemoryBacking {
match self.mem.is_sealed_memfd() {
true => ShmMemoryBacking::Ptr(self.data),
false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset),
}
}
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
self.access(f).map_err(|e| e.into())
}
}

8
crates/cmm/Cargo.toml Normal file
View file

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

View file

@ -1,15 +1,13 @@
use {
crate::{
cmm::{
cmm_eotf::Eotf,
cmm_luminance::{Luminance, TargetLuminance, white_balance},
cmm_manager::Shared,
cmm_primaries::{NamedPrimaries, Primaries},
cmm_render_intent::RenderIntent,
cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment},
},
utils::ordered_float::F64,
cmm_eotf::Eotf,
cmm_luminance::{Luminance, TargetLuminance, white_balance},
cmm_manager::Shared,
cmm_primaries::{NamedPrimaries, Primaries},
cmm_render_intent::RenderIntent,
cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment},
},
jay_utils::ordered_float::F64,
std::rc::Rc,
};

View file

@ -1,4 +1,4 @@
use crate::utils::ordered_float::F32;
use jay_utils::ordered_float::F32;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Eotf {

View file

@ -1,10 +1,8 @@
use crate::{
cmm::{
cmm_render_intent::RenderIntent,
cmm_transform::{ColorMatrix, Xyz},
},
utils::ordered_float::F64,
cmm_render_intent::RenderIntent,
cmm_transform::{ColorMatrix, Xyz},
};
use jay_utils::ordered_float::F64;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Luminance {
@ -38,7 +36,6 @@ impl Luminance {
white: F64(203.0),
};
#[expect(dead_code)]
pub const HLG: Self = Self {
min: F64(0.005),
max: F64(1000.0),

View file

@ -1,16 +1,14 @@
use {
crate::{
cmm::{
cmm_description::{
ColorDescription, ColorDescriptionIds, LinearColorDescription,
LinearColorDescriptionId, LinearColorDescriptionIds,
},
cmm_eotf::Eotf,
cmm_luminance::{Luminance, TargetLuminance},
cmm_primaries::{NamedPrimaries, Primaries},
cmm_description::{
ColorDescription, ColorDescriptionIds, LinearColorDescription,
LinearColorDescriptionId, LinearColorDescriptionIds,
},
utils::{copyhashmap::CopyHashMap, numcell::NumCell, ordered_float::F64},
cmm_eotf::Eotf,
cmm_luminance::{Luminance, TargetLuminance},
cmm_primaries::{NamedPrimaries, Primaries},
},
jay_utils::{copyhashmap::CopyHashMap, numcell::NumCell, ordered_float::F64},
std::rc::{Rc, Weak},
};

View file

@ -1,4 +1,4 @@
use {crate::utils::ordered_float::F64, std::hash::Hash};
use {jay_utils::ordered_float::F64, std::hash::Hash};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum NamedPrimaries {

View file

@ -1,11 +1,3 @@
use crate::{
ifs::color_management::{
ABSOLUTE_NO_ADAPTATION_SINCE, RENDER_INTENT_ABSOLUTE_NO_ADAPTATION,
RENDER_INTENT_PERCEPTUAL, RENDER_INTENT_RELATIVE, RENDER_INTENT_RELATIVE_BPC,
},
object::Version,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
pub enum RenderIntent {
#[default]
@ -16,19 +8,6 @@ pub enum RenderIntent {
}
impl RenderIntent {
pub fn from_wayland(intent: u32, version: Version) -> Option<Self> {
let res = match intent {
RENDER_INTENT_PERCEPTUAL => Self::Perceptual,
RENDER_INTENT_RELATIVE => Self::Relative,
RENDER_INTENT_RELATIVE_BPC => Self::RelativeBpc,
RENDER_INTENT_ABSOLUTE_NO_ADAPTATION if version >= ABSOLUTE_NO_ADAPTATION_SINCE => {
Self::AbsoluteNoAdaptation
}
_ => return None,
};
Some(res)
}
pub fn black_point_compensation(self) -> bool {
match self {
RenderIntent::Perceptual => true,

View file

@ -1,5 +1,5 @@
mod matrices {
use crate::{cmm::cmm_primaries::Primaries, utils::ordered_float::F64};
use {crate::cmm_primaries::Primaries, jay_utils::ordered_float::F64};
fn check(primaries: Primaries, expected: [[f64; 4]; 3]) {
let (ltg, gtl) = primaries.matrices();
@ -134,7 +134,7 @@ mod matrices {
}
mod transforms {
use crate::cmm::{
use crate::{
cmm_eotf::Eotf, cmm_luminance::Luminance, cmm_manager::ColorManager,
cmm_primaries::Primaries, cmm_render_intent::RenderIntent,
};

View file

@ -1,10 +1,6 @@
use {
crate::{
cmm::{cmm_eotf::Eotf, cmm_primaries::Primaries},
gfx_api::AlphaMode,
theme::Color,
utils::ordered_float::F64,
},
crate::cmm_primaries::Primaries,
jay_utils::ordered_float::F64,
std::{
fmt,
fmt::{Debug, Formatter},
@ -129,29 +125,6 @@ impl<T, U> Mul<[f64; 3]> for ColorMatrix<T, U> {
}
}
impl<T, U> Mul<Color> for ColorMatrix<T, U> {
type Output = Color;
fn mul(self, rhs: Color) -> Self::Output {
let mut rgba = rhs.to_array(Eotf::Linear);
let a = rgba[3];
if a < 1.0 && a > 0.0 {
for c in &mut rgba[..3] {
*c /= a;
}
}
let [r, g, b] = self * [rgba[0] as f64, rgba[1] as f64, rgba[2] as f64];
Color::new(
Eotf::Linear,
AlphaMode::Straight,
r as f32,
g as f32,
b as f32,
a,
)
}
}
impl<T, U> ColorMatrix<T, U> {
pub const fn new(m: [[f64; 4]; 3]) -> Self {
let m = [

53
crates/cmm/src/lib.rs Normal file
View file

@ -0,0 +1,53 @@
macro_rules! linear_ids {
($ids:ident, $id:ident, $ty:ty $(,)?) => {
#[derive(Debug)]
pub struct $ids {
next: jay_utils::numcell::NumCell<$ty>,
}
impl Default for $ids {
fn default() -> Self {
Self {
next: jay_utils::numcell::NumCell::new(1),
}
}
}
impl $ids {
pub fn next(&self) -> $id {
$id(self.next.fetch_add(1))
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct $id($ty);
impl $id {
#[allow(dead_code)]
pub fn raw(&self) -> $ty {
self.0
}
#[allow(dead_code)]
pub fn from_raw(id: $ty) -> Self {
Self(id)
}
}
impl std::fmt::Display for $id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
};
}
pub mod cmm_description;
pub mod cmm_eotf;
pub mod cmm_luminance;
pub mod cmm_manager;
pub mod cmm_primaries;
pub mod cmm_render_intent;
#[cfg(test)]
mod cmm_tests;
pub mod cmm_transform;

View file

@ -0,0 +1,24 @@
[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,8 +1,6 @@
use {
crate::{
cpu_worker::{AsyncCpuWork, CpuWork},
rect::Rect,
},
crate::{AsyncCpuWork, CpuWork},
jay_geometry::Rect,
std::ptr,
};
@ -34,7 +32,7 @@ impl ImgCopyWork {
impl CpuWork for ImgCopyWork {
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
zone!("ImgCopyWork");
jay_tracy::zone!("ImgCopyWork");
for rect in &self.rects {
let mut offset = rect.y1() * self.stride + rect.x1() * self.bpp;
if rect.width() == self.width {

View file

@ -1,9 +1,7 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
cpu_worker::{AsyncCpuWork, CompletedWork, CpuWork, WorkCompletion},
io_uring::{IoUring, IoUringError, IoUringTaskId},
},
crate::{AsyncCpuWork, CompletedWork, CpuWork, WorkCompletion},
jay_async_engine::{AsyncEngine, SpawnedFuture},
jay_io_uring::{IoUring, IoUringError, IoUringTaskId},
std::{
any::Any,
ptr,

View file

@ -3,19 +3,18 @@ pub mod jobs;
mod tests;
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
io_uring::IoUring,
utils::{
buf::TypedBuf,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
oserror::{OsError, OsErrorExt2},
pipe::{Pipe, pipe},
ptr_ext::MutPtrExt,
queue::AsyncQueue,
stack::Stack,
},
jay_async_engine::{AsyncEngine, SpawnedFuture},
jay_io_uring::IoUring,
jay_utils::{
buf::TypedBuf,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
numcell::NumCell,
oserror::{OsError, OsErrorExt2},
pipe::{Pipe, pipe},
ptr_ext::MutPtrExt,
queue::AsyncQueue,
stack::Stack,
},
parking_lot::{Condvar, Mutex},
std::{
@ -138,7 +137,27 @@ struct CpuWorkerData {
sync_wake_condvar: Arc<Condvar>,
}
linear_ids!(CpuJobIds, CpuJobId, u64);
#[derive(Debug)]
struct CpuJobIds {
next: NumCell<u64>,
}
impl Default for CpuJobIds {
fn default() -> Self {
Self {
next: NumCell::new(1),
}
}
}
impl CpuJobIds {
fn next(&self) -> CpuJobId {
CpuJobId(self.next.fetch_add(1))
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct CpuJobId(u64);
#[derive(Debug, Error)]
pub enum CpuWorkerError {

View file

@ -1,11 +1,9 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
cpu_worker::{AsyncCpuWork, CompletedWork, CpuJob, CpuWork, CpuWorker, WorkCompletion},
io_uring::IoUring,
utils::asyncevent::AsyncEvent,
wheel::Wheel,
},
crate::{AsyncCpuWork, CompletedWork, CpuJob, CpuWork, CpuWorker, WorkCompletion},
jay_async_engine::{AsyncEngine, SpawnedFuture},
jay_io_uring::IoUring,
jay_utils::asyncevent::AsyncEvent,
jay_wheel::Wheel,
std::{future::pending, rc::Rc, sync::Arc},
uapi::{OwnedFd, c::EFD_CLOEXEC},
};

View file

@ -0,0 +1,12 @@
[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,5 +1,5 @@
use {
crate::criteria::{
crate::{
CritMatcherId,
crit_graph::{CritTarget, crit_upstream::CritUpstreamNode},
},

View file

@ -1,5 +1,5 @@
use {
crate::criteria::{
crate::{
CritUpstreamNode,
crit_graph::{
CritDownstream, CritDownstreamData, CritTarget, CritUpstreamData,

View file

@ -1,5 +1,5 @@
use {
crate::criteria::{
crate::{
CritMatcherId, CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
crit_graph::{
CritTarget, CritUpstreamData,

View file

@ -1,11 +1,9 @@
use {
crate::{
criteria::{
CritDestroyListener, CritMatcherId, FixedRootMatcher, crit_leaf::CritLeafEvent,
crit_matchers::critm_constant::CritMatchConstant,
},
utils::{copyhashmap::CopyHashMap, queue::AsyncQueue},
CritDestroyListener, CritMatcherId, FixedRootMatcher, crit_leaf::CritLeafEvent,
crit_matchers::critm_constant::CritMatchConstant,
},
jay_utils::{copyhashmap::CopyHashMap, queue::AsyncQueue},
std::{
hash::Hash,
rc::{Rc, Weak},

View file

@ -1,16 +1,14 @@
use {
crate::{
criteria::{
CritDestroyListener, CritMatcherId,
crit_graph::{
WeakCritTargetOwner,
crit_downstream::CritDownstream,
crit_target::{CritTarget, CritTargetOwner},
},
crit_per_target_data::CritPerTargetData,
CritDestroyListener, CritMatcherId,
crit_graph::{
WeakCritTargetOwner,
crit_downstream::CritDownstream,
crit_target::{CritTarget, CritTargetOwner},
},
utils::copyhashmap::CopyHashMap,
crit_per_target_data::CritPerTargetData,
},
jay_utils::copyhashmap::CopyHashMap,
std::{
cell::RefMut,
mem,

View file

@ -1,12 +1,10 @@
use {
crate::{
criteria::{
CritUpstreamNode,
crit_graph::{CritDownstream, CritDownstreamData, CritMgr, CritTarget},
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
},
utils::{cell_ext::CellExt, queue::AsyncQueue},
CritUpstreamNode,
crit_graph::{CritDownstream, CritDownstreamData, CritMgr, CritTarget},
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
},
jay_utils::{cell_ext::CellExt, queue::AsyncQueue},
std::{
cell::Cell,
rc::{Rc, Weak},
@ -24,7 +22,7 @@ where
events: Rc<AsyncQueue<CritLeafEvent<Target>>>,
}
pub(in crate::criteria) struct NodeHolder<Target>
pub struct NodeHolder<Target>
where
Target: CritTarget,
{
@ -77,7 +75,7 @@ impl<Target> CritLeafMatcher<Target>
where
Target: CritTarget,
{
pub(in crate::criteria) fn new(
pub(crate) fn new(
mgr: &Target::Mgr,
upstream: &Rc<dyn CritUpstreamNode<Target>>,
on_match: impl Fn(Target::LeafData) -> Box<dyn FnOnce()> + 'static,

View file

@ -1,5 +1,5 @@
use {
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
crate::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
std::{marker::PhantomData, rc::Rc},
};

View file

@ -1,5 +1,5 @@
use {
crate::criteria::{
crate::{
CritMatcherIds, FixedRootMatcher,
crit_graph::{
CritFixedRootCriterion, CritFixedRootCriterionBase, CritMgr, CritRoot, CritRootFixed,

View file

@ -1,5 +1,5 @@
use {
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
crate::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
std::{marker::PhantomData, rc::Rc},
};

View file

@ -1,5 +1,5 @@
use {
crate::criteria::{
crate::{
CritLiteralOrRegex, RootMatcherMap,
crit_graph::{CritRootCriterion, CritTarget},
},

View file

@ -1,5 +1,5 @@
use {
crate::criteria::{
crate::{
CritMatcherId,
crit_graph::{CritTarget, CritTargetOwner, WeakCritTargetOwner},
},
@ -28,7 +28,7 @@ where
data: T,
}
pub(super) trait CritDestroyListenerBase<Target>: 'static
pub trait CritDestroyListenerBase<Target>: 'static
where
Target: CritTarget,
{

131
crates/criteria/src/lib.rs Normal file
View file

@ -0,0 +1,131 @@
pub mod crit_graph;
pub mod crit_leaf;
pub mod crit_matchers;
pub mod crit_per_target_data;
use {
crate::{
crit_graph::{CritMgr, CritMiddle, CritRoot, CritRootCriterion, CritRootFixed},
crit_leaf::CritLeafMatcher,
crit_matchers::{critm_any_or_all::CritMatchAnyOrAll, critm_exactly::CritMatchExactly},
},
jay_utils::{copyhashmap::CopyHashMap, numcell::NumCell},
linearize::StaticMap,
regex::Regex,
std::rc::{Rc, Weak},
};
pub use {
crit_graph::{CritTarget, CritUpstreamNode},
crit_per_target_data::CritDestroyListener,
};
#[derive(Debug)]
pub struct CritMatcherIds {
next: NumCell<u64>,
}
impl Default for CritMatcherIds {
fn default() -> Self {
Self {
next: NumCell::new(1),
}
}
}
impl CritMatcherIds {
pub fn next(&self) -> CritMatcherId {
CritMatcherId(self.next.fetch_add(1))
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CritMatcherId(u64);
impl CritMatcherId {
#[allow(clippy::allow_attributes, dead_code)]
pub fn raw(&self) -> u64 {
self.0
}
#[allow(clippy::allow_attributes, dead_code)]
pub fn from_raw(id: u64) -> Self {
Self(id)
}
}
impl std::fmt::Display for CritMatcherId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
pub type RootMatcherMap<Target, T> = CopyHashMap<CritMatcherId, Weak<CritRoot<Target, T>>>;
pub type FixedRootMatcher<Target, T> =
StaticMap<bool, Rc<CritRoot<Target, CritRootFixed<Target, T>>>>;
#[derive(Clone)]
pub enum CritLiteralOrRegex {
Literal(String),
Regex(Regex),
}
impl CritLiteralOrRegex {
fn matches(&self, string: &str) -> bool {
match self {
CritLiteralOrRegex::Literal(p) => string == p,
CritLiteralOrRegex::Regex(r) => r.is_match(string),
}
}
}
pub trait CritMgrExt: CritMgr {
fn list(
&self,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
all: bool,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
if upstream.is_empty() {
return self.match_constant()[all].clone();
}
CritMiddle::new(self, upstream, CritMatchAnyOrAll::new(upstream, all))
}
fn exactly(
&self,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
num: usize,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
if num > upstream.len() {
return self.match_constant()[false].clone();
}
if num == 0 {
let upstream: Vec<_> = upstream.iter().map(|u| u.not(self)).collect();
return self.list(&upstream, true);
}
CritMiddle::new(self, upstream, CritMatchExactly::new(upstream, num))
}
fn leaf(
&self,
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
on_match: impl Fn(<Self::Target as CritTarget>::LeafData) -> Box<dyn FnOnce()> + 'static,
) -> Rc<CritLeafMatcher<Self::Target>> {
CritLeafMatcher::new(self, upstream, on_match)
}
fn not(
&self,
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
upstream.not(self)
}
fn root<T>(&self, criterion: T) -> Rc<dyn CritUpstreamNode<Self::Target>>
where
T: CritRootCriterion<Self::Target>,
{
CritRoot::new(self.roots(), self.id(), criterion)
}
}
impl<T> CritMgrExt for T where T: CritMgr {}

10
crates/damage/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[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" }

116
crates/damage/src/lib.rs Normal file
View file

@ -0,0 +1,116 @@
use {
jay_geometry::Rect,
jay_tree_types::Transform,
jay_units::fixed::Fixed,
};
#[derive(Copy, Clone, Debug)]
pub struct DamageMatrix {
transform: Transform,
mx: f64,
my: f64,
dx: f64,
dy: f64,
smear: i32,
}
impl Default for DamageMatrix {
fn default() -> Self {
Self {
transform: Default::default(),
mx: 1.0,
my: 1.0,
dx: 0.0,
dy: 0.0,
smear: 0,
}
}
}
impl DamageMatrix {
pub fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
let x1 = rect.x1() - self.smear;
let x2 = rect.x2() + self.smear;
let y1 = rect.y1() - self.smear;
let y2 = rect.y2() + self.smear;
let [x1, y1, x2, y2] = match self.transform {
Transform::None => [x1, y1, x2, y2],
Transform::Rotate90 => [-y2, x1, -y1, x2],
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
Transform::Rotate270 => [y1, -x2, y2, -x1],
Transform::Flip => [-x2, y1, -x1, y2],
Transform::FlipRotate90 => [y1, x1, y2, x2],
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
};
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
Rect::new_saturating(x1, y1, x2, y2)
}
pub fn new(
transform: Transform,
legacy_scale: i32,
buffer_width: i32,
buffer_height: i32,
viewport: Option<[Fixed; 4]>,
dst_width: i32,
dst_height: i32,
) -> DamageMatrix {
let mut buffer_width = buffer_width as f64;
let mut buffer_height = buffer_height as f64;
let dst_width = dst_width as f64;
let dst_height = dst_height as f64;
let mut mx = 1.0;
let mut my = 1.0;
if legacy_scale != 1 {
let scale_inv = 1.0 / (legacy_scale as f64);
mx = scale_inv;
my = scale_inv;
buffer_width *= scale_inv;
buffer_height *= scale_inv;
}
let (mut buffer_width, mut buffer_height) =
transform.maybe_swap((buffer_width, buffer_height));
let (mut dx, mut dy) = match transform {
Transform::None => (0.0, 0.0),
Transform::Rotate90 => (buffer_width, 0.0),
Transform::Rotate180 => (buffer_width, buffer_height),
Transform::Rotate270 => (0.0, buffer_height),
Transform::Flip => (buffer_width, 0.0),
Transform::FlipRotate90 => (0.0, 0.0),
Transform::FlipRotate180 => (0.0, buffer_height),
Transform::FlipRotate270 => (buffer_width, buffer_height),
};
if let Some([x, y, w, h]) = viewport {
dx -= x.to_f64();
dy -= y.to_f64();
buffer_width = w.to_f64();
buffer_height = h.to_f64();
}
let mut smear = false;
if dst_width != buffer_width {
let scale = dst_width / buffer_width;
mx *= scale;
dx *= scale;
smear |= dst_width > buffer_width;
}
if dst_height != buffer_height {
let scale = dst_height / buffer_height;
my *= scale;
dy *= scale;
smear |= dst_height > buffer_height;
}
DamageMatrix {
transform,
mx,
my,
dx,
dy,
smear: smear as _,
}
}
}

View file

@ -0,0 +1,14 @@
[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

@ -3,10 +3,8 @@ use {
TY_ARRAY, TY_BOOLEAN, TY_BYTE, TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH,
TY_SIGNATURE, TY_STRING, TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
},
crate::{
dbus::{DbusError, DynamicType, Parser, types::Variant},
utils::buf::DynamicBuf,
},
crate::{types::Variant, DbusError, DynamicType, Parser},
jay_utils::buf::DynamicBuf,
std::ops::Deref,
};
@ -156,11 +154,8 @@ impl DynamicType {
}
let mut vals = vec![];
{
let mut parser = Parser {
buf: &parser.buf[..parser.pos + len],
pos: parser.pos,
fds: parser.fds,
};
let mut parser =
Parser::new_at(&parser.buf[..parser.pos + len], parser.pos, parser.fds);
while !parser.eof() {
vals.push(el.parse(&mut parser)?);
}

View file

@ -1,8 +1,6 @@
use {
crate::{
dbus::{DbusType, Formatter, types::Variant},
utils::buf::DynamicBuf,
},
crate::{types::Variant, DbusType, Formatter},
jay_utils::buf::DynamicBuf,
std::rc::Rc,
uapi::{OwnedFd, Packed},
};

232
crates/dbus-core/src/lib.rs Normal file
View file

@ -0,0 +1,232 @@
pub use {property::{Get, GetReply}, types::*};
use {
jay_bufio::BufIoError,
jay_io_uring::IoUringError,
jay_utils::{buf::DynamicBuf, oserror::OsError},
std::{borrow::Cow, fmt::Display, rc::Rc},
thiserror::Error,
uapi::OwnedFd,
};
mod dynamic_type;
mod formatter;
mod parser;
pub mod property;
pub mod types;
#[derive(Debug)]
pub struct CallError {
pub name: String,
pub msg: Option<String>,
}
impl Display for CallError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(msg) = &self.msg {
write!(f, "{}: {}", self.name, msg)
} else {
write!(f, "{}", self.name)
}
}
}
#[derive(Debug, Error)]
pub enum DbusError {
#[error("Encountered an unknown type in a signature")]
UnknownType,
#[error("Function call reply does not contain a reply serial")]
NoReplySerial,
#[error("Signal message contains no interface or member or path")]
MissingSignalHeaders,
#[error("Method call message contains no interface or member or path")]
MissingMethodCallHeaders,
#[error("Error has no error name")]
NoErrorName,
#[error("The socket was killed")]
Killed,
#[error("{0}")]
CallError(CallError),
#[error("FD index is out of bounds")]
OobFds,
#[error("Variant has an invalid type")]
InvalidVariantType,
#[error("Could not create a socket")]
Socket(#[source] OsError),
#[error("Could not connect")]
Connect(#[source] IoUringError),
#[error("Could not write to the dbus socket")]
WriteError(#[source] IoUringError),
#[error("Could not read from the dbus socket")]
ReadError(#[source] IoUringError),
#[error("timeout")]
IoUringError(#[source] Box<IoUringError>),
#[error("Server did not send auth challenge")]
NoChallenge,
#[error("Server did not accept our authentication")]
Auth,
#[error("Array length is not a multiple of the element size")]
PodArrayLength,
#[error("Peer did not send enough fds")]
TooFewFds,
#[error("Variant signature is not a single type")]
TrailingVariantSignature,
#[error("Dict signature does not contain a terminating '}}'")]
UnterminatedDict,
#[error("Struct signature does not contain a terminating '}}'")]
UnterminatedStruct,
#[error("Dict signature contains trailing types")]
DictTrailing,
#[error("String does not contain valid UTF-8")]
InvalidUtf8,
#[error("Unexpected end of message")]
UnexpectedEof,
#[error("Boolean value was not 0 or 1")]
InvalidBoolValue,
#[error("Signature is empty")]
EmptySignature,
#[error("The session bus address is not set")]
SessionBusAddressNotSet,
#[error("Server does not support FD passing")]
UnixFd,
#[error("Server message has a different endianess than ourselves")]
InvalidEndianess,
#[error("Server speaks an unexpected protocol version")]
InvalidProtocol,
#[error("Signature contains an invalid type")]
InvalidSignatureType,
#[error("The signal already has a handler")]
AlreadyHandled,
#[error(transparent)]
BufIoError(#[from] BufIoError),
#[error(transparent)]
DbusError(Rc<DbusError>),
}
impl From<IoUringError> for DbusError {
fn from(e: IoUringError) -> Self {
DbusError::IoUringError(Box::new(e))
}
}
const TY_BYTE: u8 = b'y';
const TY_BOOLEAN: u8 = b'b';
const TY_INT16: u8 = b'n';
const TY_UINT16: u8 = b'q';
const TY_INT32: u8 = b'i';
const TY_UINT32: u8 = b'u';
const TY_INT64: u8 = b'x';
const TY_UINT64: u8 = b't';
const TY_DOUBLE: u8 = b'd';
const TY_STRING: u8 = b's';
const TY_OBJECT_PATH: u8 = b'o';
const TY_SIGNATURE: u8 = b'g';
const TY_ARRAY: u8 = b'a';
const TY_VARIANT: u8 = b'v';
const TY_UNIX_FD: u8 = b'h';
#[derive(Clone, Debug)]
pub enum DynamicType {
U8,
Bool,
I16,
U16,
I32,
U32,
I64,
U64,
F64,
String,
ObjectPath,
Signature,
Variant,
Fd,
Array(Box<DynamicType>),
DictEntry(Box<DynamicType>, Box<DynamicType>),
Struct(Vec<DynamicType>),
}
pub struct Parser<'a> {
pub(crate) buf: &'a [u8],
pub(crate) pos: usize,
pub(crate) fds: &'a [Rc<OwnedFd>],
}
pub struct Formatter<'a> {
fds: &'a mut Vec<Rc<OwnedFd>>,
buf: &'a mut DynamicBuf,
}
pub unsafe trait Message<'a>: Sized + 'a {
const SIGNATURE: &'static str;
const INTERFACE: &'static str;
const MEMBER: &'static str;
type Generic<'b>: Message<'b>;
fn marshal(&self, w: &mut Formatter);
fn unmarshal(p: &mut Parser<'a>) -> Result<Self, DbusError>;
fn num_fds(&self) -> u32;
}
pub struct ErrorMessage<'a> {
pub msg: Cow<'a, str>,
}
unsafe impl<'a> Message<'a> for ErrorMessage<'a> {
const SIGNATURE: &'static str = "s";
const INTERFACE: &'static str = "";
const MEMBER: &'static str = "";
type Generic<'b> = ErrorMessage<'b>;
fn marshal(&self, w: &mut Formatter) {
self.msg.marshal(w)
}
fn unmarshal(p: &mut Parser<'a>) -> Result<Self, DbusError> {
Ok(Self {
msg: p.unmarshal()?,
})
}
fn num_fds(&self) -> u32 {
0
}
}
pub trait Property {
const INTERFACE: &'static str;
const PROPERTY: &'static str;
type Type: DbusType<'static>;
}
pub trait Signal<'a>: Message<'a> {}
pub trait MethodCall<'a>: Message<'a> {
type Reply: Message<'static>;
}
pub unsafe trait DbusType<'a>: Clone + 'a {
const ALIGNMENT: usize;
const IS_POD: bool;
type Generic<'b>: DbusType<'b> + 'b;
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError>;
#[allow(clippy::allow_attributes, dead_code)]
fn write_signature(w: &mut Vec<u8>);
fn marshal(&self, fmt: &mut Formatter);
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError>;
fn num_fds(&self) -> u32 {
0
}
}
pub mod prelude {
pub use {
super::{
DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, Signal,
types::{Bool, DictEntry, ObjectPath, Variant},
},
std::{borrow::Cow, rc::Rc},
uapi::OwnedFd,
};
}

View file

@ -1,7 +1,7 @@
use {
crate::dbus::{
crate::{
types::{Bool, ObjectPath, Signature, Variant, FALSE, TRUE},
DbusError, DbusType, DynamicType, Parser,
types::{Bool, FALSE, ObjectPath, Signature, TRUE, Variant},
},
bstr::ByteSlice,
std::{borrow::Cow, rc::Rc},
@ -10,7 +10,11 @@ use {
impl<'a> Parser<'a> {
pub fn new(buf: &'a [u8], fds: &'a [Rc<OwnedFd>]) -> Self {
Self { buf, pos: 0, fds }
Self::new_at(buf, 0, fds)
}
pub fn new_at(buf: &'a [u8], pos: usize, fds: &'a [Rc<OwnedFd>]) -> Self {
Self { buf, pos, fds }
}
pub fn eof(&self) -> bool {
@ -107,11 +111,7 @@ impl<'a> Parser<'a> {
self.pos += len;
Ok(Cow::Borrowed(slice))
} else {
let mut parser = Parser {
buf: &self.buf[..self.pos + len],
pos: self.pos,
fds: self.fds,
};
let mut parser = Parser::new_at(&self.buf[..self.pos + len], self.pos, self.fds);
self.pos += len;
let mut res = vec![];
while !parser.eof() {

View file

@ -1,5 +1,5 @@
use {
crate::dbus::{DbusError, DbusType, Formatter, Message, MethodCall, Parser},
crate::{DbusError, DbusType, Formatter, Message, MethodCall, Parser},
std::{borrow::Cow, marker::PhantomData},
};

View file

@ -1,12 +1,10 @@
use {
crate::{
dbus::{
DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE,
TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING,
TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
},
utils::buf::DynamicBuf,
DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE,
TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING,
TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
},
jay_utils::buf::DynamicBuf,
std::{borrow::Cow, ops::Deref, rc::Rc},
uapi::{OwnedFd, Packed, Pod},
};
@ -501,31 +499,6 @@ impl<'a> Variant<'a> {
w.push(c);
}
pub fn borrow<'b>(&'b self) -> Variant<'b> {
match self {
Variant::U8(v) => Variant::U8(*v),
Variant::Bool(v) => Variant::Bool(*v),
Variant::I16(v) => Variant::I16(*v),
Variant::U16(v) => Variant::U16(*v),
Variant::I32(v) => Variant::I32(*v),
Variant::U32(v) => Variant::U32(*v),
Variant::I64(v) => Variant::I64(*v),
Variant::U64(v) => Variant::U64(*v),
Variant::F64(v) => Variant::F64(*v),
Variant::String(v) => Variant::String(v.deref().into()),
Variant::ObjectPath(v) => Variant::ObjectPath(ObjectPath(v.0.deref().into())),
Variant::Signature(v) => Variant::Signature(Signature(v.0.deref().into())),
Variant::Variant(v) => Variant::Variant(Box::new(v.deref().borrow())),
Variant::Fd(v) => Variant::Fd(v.clone()),
Variant::Array(t, v) => {
Variant::Array(t.clone(), v.iter().map(|v| v.borrow()).collect())
}
Variant::DictEntry(k, v) => {
Variant::DictEntry(Box::new(k.deref().borrow()), Box::new(v.deref().borrow()))
}
Variant::Struct(v) => Variant::Struct(v.iter().map(|v| v.borrow()).collect()),
}
}
}
unsafe impl<'a> DbusType<'a> for Variant<'a> {

View file

@ -0,0 +1,13 @@
[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

@ -0,0 +1,149 @@
use {
ahash::AHashMap,
byteorder::{NativeEndian, WriteBytesExt},
jay_video_types::Modifier,
std::{io::Write, rc::Rc},
thiserror::Error,
uapi::{OwnedFd, c},
};
#[derive(Debug)]
pub struct DrmFeedbackIds {
next: std::cell::Cell<u64>,
}
impl Default for DrmFeedbackIds {
fn default() -> Self {
Self {
next: std::cell::Cell::new(1),
}
}
}
impl DrmFeedbackIds {
pub fn next(&self) -> DrmFeedbackId {
let id = self.next.get();
self.next.set(id + 1);
DrmFeedbackId(id)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct DrmFeedbackId(u64);
#[derive(Debug)]
pub struct DrmFeedbackShared {
pub fd: Rc<OwnedFd>,
pub size: usize,
pub main_device: c::dev_t,
pub indices: AHashMap<(u32, Modifier), u16>,
}
#[derive(Debug)]
pub struct DrmFeedback {
pub id: DrmFeedbackId,
pub shared: Rc<DrmFeedbackShared>,
pub tranches: Vec<DrmFeedbackTranche>,
}
#[derive(Clone, Debug)]
pub struct DrmFeedbackTranche {
pub device: c::dev_t,
pub indices: Vec<u16>,
pub scanout: bool,
}
impl DrmFeedback {
pub fn new<C: DrmFeedbackContext + ?Sized>(
ids: &DrmFeedbackIds,
render_ctx: &C,
) -> Result<Self, DrmFeedbackError> {
let main_device = match render_ctx.main_device() {
Some(dev) => dev,
_ => return Err(DrmFeedbackError::NoDrmDevice),
};
let (data, index_map) = create_fd_data(render_ctx);
let mut memfd =
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
memfd.write_all(&data).unwrap();
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap();
uapi::fcntl_add_seals(
memfd.raw(),
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
)
.unwrap();
Ok(Self {
id: ids.next(),
tranches: vec![DrmFeedbackTranche {
device: main_device,
indices: (0..index_map.len()).map(|v| v as u16).collect(),
scanout: false,
}],
shared: Rc::new(DrmFeedbackShared {
fd: Rc::new(memfd),
size: data.len(),
main_device,
indices: index_map,
}),
})
}
pub fn for_scanout(
&self,
ids: &DrmFeedbackIds,
devnum: c::dev_t,
formats: &[(u32, Modifier)],
) -> Result<Option<Self>, DrmFeedbackError> {
let mut tranches = vec![];
{
let mut indices = vec![];
for (format, modifier) in formats {
if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) {
indices.push(*idx);
}
}
if indices.len() > 0 {
tranches.push(DrmFeedbackTranche {
device: devnum,
indices,
scanout: true,
});
} else {
return Ok(None);
}
}
tranches.extend(self.tranches.iter().cloned());
Ok(Some(Self {
id: ids.next(),
shared: self.shared.clone(),
tranches,
}))
}
}
pub trait DrmFeedbackContext {
fn main_device(&self) -> Option<c::dev_t>;
fn for_each_read_format(&self, f: &mut dyn FnMut(u32, Modifier));
}
fn create_fd_data<C: DrmFeedbackContext + ?Sized>(
ctx: &C,
) -> (Vec<u8>, AHashMap<(u32, Modifier), u16>) {
let mut vec = vec![];
let mut map = AHashMap::new();
let mut pos = 0;
ctx.for_each_read_format(&mut |format, modifier| {
vec.write_u32::<NativeEndian>(format).unwrap();
vec.write_u32::<NativeEndian>(0).unwrap();
vec.write_u64::<NativeEndian>(modifier).unwrap();
map.insert((format, modifier), pos);
pos += 1;
});
(vec, map)
}
#[derive(Debug, Error)]
pub enum DrmFeedbackError {
#[error("Graphics API does not have a DRM device")]
NoDrmDevice,
}

11
crates/edid/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[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,15 +1,48 @@
use {
crate::utils::{
bitflags::BitflagsExt, clonecell::UnsafeCellCloneSafe, ptr_ext::PtrExt, stack::Stack,
},
bstr::{BString, ByteSlice},
std::{
cell::RefCell,
fmt::{Debug, Formatter},
rc::Rc,
},
thiserror::Error,
};
trait BitflagsExt {
fn contains(self, other: Self) -> bool;
}
impl BitflagsExt for u8 {
fn contains(self, other: Self) -> bool {
self & other == other
}
}
struct Stack<T>(RefCell<Vec<T>>);
impl<T> Default for Stack<T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T> Stack<T> {
fn push(&self, v: T) {
self.0.borrow_mut().push(v);
}
fn pop(&self) -> Option<T> {
self.0.borrow_mut().pop()
}
fn to_vec(&self) -> Vec<T>
where
T: Clone,
{
self.0.borrow().clone()
}
}
#[derive(Copy, Clone, Debug)]
pub enum ColorBitDepth {
Undefined,
@ -30,7 +63,6 @@ pub enum DigitalVideoInterfaceStandard {
HdmiB,
MDDI,
DisplayPort,
#[expect(dead_code)]
Unknown(u8),
}
@ -50,7 +82,6 @@ impl Debug for SignalLevelStandard {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub enum VideoInputDefinition {
Analog {
signal_level_standard: SignalLevelStandard,
@ -87,7 +118,6 @@ pub struct ChromaticityCoordinates {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct EstablishedTimings {
pub s_720x400_70: bool,
pub s_720x400_88: bool,
@ -118,7 +148,6 @@ pub enum AspectRatio {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct StandardTiming {
pub x_resolution: u16,
pub aspect_ratio: AspectRatio,
@ -132,7 +161,6 @@ pub enum AnalogSyncType {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub enum SyncSignal {
Analog {
ty: AnalogSyncType,
@ -184,7 +212,6 @@ impl Debug for StereoViewingSupport {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct DisplayRangeLimitsAndAdditionalTiming {
pub vertical_field_rate_min: u16,
pub vertical_field_rate_max: u16,
@ -201,12 +228,10 @@ pub enum AspectRatioPreference {
A16_10,
A5_4,
A15_9,
#[expect(dead_code)]
Unknown(u8),
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub enum ExtendedTimingInformation {
DefaultGtf,
NoTimingInformation,
@ -240,7 +265,6 @@ pub enum ExtendedTimingInformation {
}
#[derive(Copy, Clone, Debug, Default)]
#[expect(dead_code)]
pub struct ColorPoint {
pub white_point_index: u8,
pub white_point_x: u16,
@ -249,7 +273,6 @@ pub struct ColorPoint {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct EstablishedTimings3 {
pub s640x350_85: bool,
pub s640x400_85: bool,
@ -298,7 +321,6 @@ pub struct EstablishedTimings3 {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct ColorManagementData {
pub red_a3: u16,
pub red_a2: u16,
@ -325,7 +347,6 @@ pub enum CvtPreferredVerticalRate {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct Cvt3ByteCode {
pub addressable_lines_per_field: u16,
pub aspect_ration: CvtAspectRatio,
@ -338,7 +359,6 @@ pub struct Cvt3ByteCode {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct DetailedTimingDescriptor {
pub pixel_clock_khz: u32,
pub horizontal_addressable_pixels: u16,
@ -359,7 +379,6 @@ pub struct DetailedTimingDescriptor {
}
#[derive(Clone, Debug)]
#[expect(dead_code)]
pub enum Descriptor {
Unknown(u8),
DetailedTimingDescriptor(DetailedTimingDescriptor),
@ -393,7 +412,6 @@ macro_rules! bail {
#[derive(Clone, Debug)]
pub enum EdidParseContext {
#[expect(dead_code)]
ReadingBytes(usize),
BaseBlock,
Descriptors,
@ -410,8 +428,6 @@ pub enum EdidParseContext {
VideoInputDefinition,
}
unsafe impl UnsafeCellCloneSafe for EdidParseContext {}
struct EdidPushedContext {
stack: Rc<Stack<(usize, EdidParseContext)>>,
}
@ -453,7 +469,7 @@ impl<'a> EdidParser<'a> {
if self.data.len() - self.pos < N {
bail!(self, EdidError::UnexpectedEof);
}
let v = unsafe { self.data[self.pos..].as_ptr().cast::<[u8; N]>().deref() };
let v = self.data[self.pos..self.pos + N].try_into().unwrap();
self.pos += N;
Ok(v)
}
@ -1161,7 +1177,6 @@ pub enum DisplayColorType {
}
#[derive(Debug)]
#[expect(dead_code)]
pub enum FeatureSupport2 {
Analog {
display_color_type: DisplayColorType,
@ -1174,7 +1189,6 @@ pub enum FeatureSupport2 {
}
#[derive(Debug)]
#[expect(dead_code)]
pub struct FeatureSupport {
pub standby_supported: bool,
pub suspend_supported: bool,
@ -1186,7 +1200,6 @@ pub struct FeatureSupport {
}
#[derive(Debug)]
#[expect(dead_code)]
pub struct EdidBaseBlock {
pub id_manufacturer_name: BString,
pub id_product_code: u16,
@ -1229,12 +1242,10 @@ pub enum CtaDataBlock {
#[derive(Debug)]
pub struct CtaAmdVendorDataBlock {
pub minimum_refresh_hz: u8,
#[expect(dead_code)]
pub maximum_refresh_hz: u8,
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct CtaColorimetryDataBlock {
pub bt2020_rgb: bool,
pub bt2020_ycc: bool,
@ -1248,7 +1259,6 @@ pub struct CtaColorimetryDataBlock {
}
#[derive(Copy, Clone, Debug)]
#[expect(dead_code)]
pub struct CtaStaticHdrMetadataDataBlock {
pub traditional_gamma_sdr_luminance: bool,
pub traditional_gamma_hdr_luminance: bool,

View file

@ -0,0 +1,14 @@
[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,14 +1,12 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
io_uring::{IoUring, IoUringError},
utils::{
buf::Buf,
errorfmt::ErrorFmt,
oserror::{OsError, OsErrorExt, OsErrorExt2},
queue::AsyncQueue,
stack::Stack,
},
jay_async_engine::{AsyncEngine, SpawnedFuture},
jay_io_uring::{IoUring, IoUringError},
jay_utils::{
buf::Buf,
errorfmt::ErrorFmt,
oserror::{OsError, OsErrorExt, OsErrorExt2},
queue::AsyncQueue,
stack::Stack,
},
std::{cell::Cell, future::poll_fn, pin::Pin, rc::Rc, slice, task::Poll},
thiserror::Error,

View file

@ -1,7 +1,8 @@
use {
crate::{
async_engine::AsyncEngine, eventfd_cache::EventfdCache, io_uring::IoUring, utils::array,
},
crate::EventfdCache,
jay_async_engine::AsyncEngine,
jay_io_uring::IoUring,
jay_utils::array,
std::{rc::Rc, slice},
uapi::c,
};

13
crates/formats/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[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" }

559
crates/formats/src/lib.rs Normal file
View file

@ -0,0 +1,559 @@
use {
ahash::AHashMap,
ash::vk,
clap::{ValueEnum, builder::PossibleValue},
jay_config::video::Format as ConfigFormat,
std::{
fmt::{self, Debug, Write},
sync::LazyLock,
},
};
pub type GLenum = u32;
pub type GLint = i32;
const GL_RGBA: GLint = 0x1908;
const GL_RGBA8: GLenum = 0x8058;
const GL_BGRA_EXT: GLint = 0x80E1;
const GL_UNSIGNED_BYTE: GLint = 0x1401;
#[derive(Copy, Clone, Debug)]
pub struct FormatShmInfo {
pub gl_format: GLint,
pub gl_internal_format: GLenum,
pub gl_type: GLint,
}
#[derive(Copy, Clone, Debug)]
pub struct Format {
pub name: &'static str,
pub vk_format: vk::Format,
pub drm: u32,
pub wl_id: Option<u32>,
pub external_only_guess: bool,
pub has_alpha: bool,
pub opaque: Option<&'static Format>,
pub shm_info: Option<FormatShmInfo>,
pub config: ConfigFormat,
pub bpp: u32,
}
const fn default(config: ConfigFormat) -> Format {
Format {
name: "",
vk_format: vk::Format::UNDEFINED,
drm: 0,
wl_id: None,
external_only_guess: false,
has_alpha: false,
opaque: None,
shm_info: None,
config,
bpp: 4,
}
}
impl PartialEq for Format {
fn eq(&self, other: &Self) -> bool {
self.drm == other.drm
}
}
impl Eq for Format {}
impl ValueEnum for &'static Format {
fn value_variants<'a>() -> &'a [Self] {
ref_formats()
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(self.name))
}
}
static FORMATS_MAP: LazyLock<AHashMap<u32, &'static Format>> = LazyLock::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.drm, format).is_none());
}
map
});
static FORMATS_REFS: LazyLock<Vec<&'static Format>> = LazyLock::new(|| FORMATS.iter().collect());
static FORMATS_NAMES: LazyLock<AHashMap<&'static str, &'static Format>> = LazyLock::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.name, format).is_none());
}
map
});
static FORMATS_CONFIG: LazyLock<AHashMap<ConfigFormat, &'static Format>> = LazyLock::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.config, format).is_none());
}
map
});
#[test]
fn formats_dont_panic() {
formats();
named_formats();
config_formats();
}
pub fn formats() -> &'static AHashMap<u32, &'static Format> {
&FORMATS_MAP
}
pub fn ref_formats() -> &'static [&'static Format] {
&FORMATS_REFS
}
pub fn named_formats() -> &'static AHashMap<&'static str, &'static Format> {
&FORMATS_NAMES
}
pub fn config_formats() -> &'static AHashMap<ConfigFormat, &'static Format> {
&FORMATS_CONFIG
}
const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
}
pub fn debug(fourcc: u32) -> impl Debug {
fmt::from_fn(move |fmt| {
fmt.write_char(fourcc as u8 as char)?;
fmt.write_char((fourcc >> 8) as u8 as char)?;
fmt.write_char((fourcc >> 16) as u8 as char)?;
fmt.write_char((fourcc >> 24) as u8 as char)?;
Ok(())
})
}
const ARGB8888_ID: u32 = 0;
const ARGB8888_DRM: u32 = fourcc_code('A', 'R', '2', '4');
const XRGB8888_ID: u32 = 1;
const XRGB8888_DRM: u32 = fourcc_code('X', 'R', '2', '4');
pub fn map_wayland_format_id(id: u32) -> u32 {
match id {
ARGB8888_ID => ARGB8888_DRM,
XRGB8888_ID => XRGB8888_DRM,
_ => id,
}
}
pub static ARGB8888: &Format = &Format {
name: "argb8888",
shm_info: Some(FormatShmInfo {
gl_format: GL_BGRA_EXT,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE,
}),
vk_format: vk::Format::B8G8R8A8_UNORM,
bpp: 4,
drm: ARGB8888_DRM,
wl_id: Some(ARGB8888_ID),
external_only_guess: false,
has_alpha: true,
opaque: Some(XRGB8888),
config: ConfigFormat::ARGB8888,
};
pub static XRGB8888: &Format = &Format {
name: "xrgb8888",
shm_info: Some(FormatShmInfo {
gl_format: GL_BGRA_EXT,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE,
}),
vk_format: vk::Format::B8G8R8A8_UNORM,
bpp: 4,
drm: XRGB8888_DRM,
wl_id: Some(XRGB8888_ID),
external_only_guess: false,
has_alpha: false,
opaque: None,
config: ConfigFormat::XRGB8888,
};
static ABGR8888: &Format = &Format {
name: "abgr8888",
shm_info: Some(FormatShmInfo {
gl_format: GL_RGBA,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE,
}),
vk_format: vk::Format::R8G8B8A8_UNORM,
bpp: 4,
drm: fourcc_code('A', 'B', '2', '4'),
wl_id: None,
external_only_guess: false,
has_alpha: true,
opaque: Some(XBGR8888),
config: ConfigFormat::ABGR8888,
};
static XBGR8888: &Format = &Format {
name: "xbgr8888",
shm_info: Some(FormatShmInfo {
gl_format: GL_RGBA,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE,
}),
vk_format: vk::Format::R8G8B8A8_UNORM,
bpp: 4,
drm: fourcc_code('X', 'B', '2', '4'),
wl_id: None,
external_only_guess: false,
has_alpha: false,
opaque: None,
config: ConfigFormat::XBGR8888,
};
static R8: &Format = &Format {
name: "r8",
vk_format: vk::Format::R8_UNORM,
bpp: 1,
drm: fourcc_code('R', '8', ' ', ' '),
..default(ConfigFormat::R8)
};
static GR88: &Format = &Format {
name: "gr88",
vk_format: vk::Format::R8G8_UNORM,
bpp: 2,
drm: fourcc_code('G', 'R', '8', '8'),
..default(ConfigFormat::GR88)
};
static RGB888: &Format = &Format {
name: "rgb888",
vk_format: vk::Format::B8G8R8_UNORM,
bpp: 3,
drm: fourcc_code('R', 'G', '2', '4'),
..default(ConfigFormat::RGB888)
};
static BGR888: &Format = &Format {
name: "bgr888",
vk_format: vk::Format::R8G8B8_UNORM,
bpp: 3,
drm: fourcc_code('B', 'G', '2', '4'),
..default(ConfigFormat::BGR888)
};
static RGBA4444: &Format = &Format {
name: "rgba4444",
vk_format: vk::Format::R4G4B4A4_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('R', 'A', '1', '2'),
has_alpha: true,
opaque: Some(RGBX4444),
..default(ConfigFormat::RGBA4444)
};
static RGBX4444: &Format = &Format {
name: "rgbx4444",
vk_format: vk::Format::R4G4B4A4_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('R', 'X', '1', '2'),
..default(ConfigFormat::RGBX4444)
};
static BGRA4444: &Format = &Format {
name: "bgra4444",
vk_format: vk::Format::B4G4R4A4_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('B', 'A', '1', '2'),
has_alpha: true,
opaque: Some(BGRX4444),
..default(ConfigFormat::BGRA4444)
};
static BGRX4444: &Format = &Format {
name: "bgrx4444",
vk_format: vk::Format::B4G4R4A4_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('B', 'X', '1', '2'),
..default(ConfigFormat::BGRX4444)
};
static RGB565: &Format = &Format {
name: "rgb565",
vk_format: vk::Format::R5G6B5_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('R', 'G', '1', '6'),
..default(ConfigFormat::RGB565)
};
static BGR565: &Format = &Format {
name: "bgr565",
vk_format: vk::Format::B5G6R5_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('B', 'G', '1', '6'),
..default(ConfigFormat::BGR565)
};
static RGBA5551: &Format = &Format {
name: "rgba5551",
vk_format: vk::Format::R5G5B5A1_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('R', 'A', '1', '5'),
has_alpha: true,
opaque: Some(RGBX5551),
..default(ConfigFormat::RGBA5551)
};
static RGBX5551: &Format = &Format {
name: "rgbx5551",
vk_format: vk::Format::R5G5B5A1_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('R', 'X', '1', '5'),
..default(ConfigFormat::RGBX5551)
};
static BGRA5551: &Format = &Format {
name: "bgra5551",
vk_format: vk::Format::B5G5R5A1_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('B', 'A', '1', '5'),
has_alpha: true,
opaque: Some(BGRX5551),
..default(ConfigFormat::BGRA5551)
};
static BGRX5551: &Format = &Format {
name: "bgrx5551",
vk_format: vk::Format::B5G5R5A1_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('B', 'X', '1', '5'),
..default(ConfigFormat::BGRX5551)
};
static ARGB1555: &Format = &Format {
name: "argb1555",
vk_format: vk::Format::A1R5G5B5_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('A', 'R', '1', '5'),
has_alpha: true,
opaque: Some(XRGB1555),
..default(ConfigFormat::ARGB1555)
};
static XRGB1555: &Format = &Format {
name: "xrgb1555",
vk_format: vk::Format::A1R5G5B5_UNORM_PACK16,
bpp: 2,
drm: fourcc_code('X', 'R', '1', '5'),
..default(ConfigFormat::XRGB1555)
};
static ARGB2101010: &Format = &Format {
name: "argb2101010",
vk_format: vk::Format::A2R10G10B10_UNORM_PACK32,
bpp: 4,
drm: fourcc_code('A', 'R', '3', '0'),
has_alpha: true,
opaque: Some(XRGB2101010),
..default(ConfigFormat::ARGB2101010)
};
static XRGB2101010: &Format = &Format {
name: "xrgb2101010",
vk_format: vk::Format::A2R10G10B10_UNORM_PACK32,
bpp: 4,
drm: fourcc_code('X', 'R', '3', '0'),
..default(ConfigFormat::XRGB2101010)
};
static ABGR2101010: &Format = &Format {
name: "abgr2101010",
vk_format: vk::Format::A2B10G10R10_UNORM_PACK32,
bpp: 4,
drm: fourcc_code('A', 'B', '3', '0'),
has_alpha: true,
opaque: Some(XBGR2101010),
..default(ConfigFormat::ABGR2101010)
};
static XBGR2101010: &Format = &Format {
name: "xbgr2101010",
vk_format: vk::Format::A2B10G10R10_UNORM_PACK32,
bpp: 4,
drm: fourcc_code('X', 'B', '3', '0'),
..default(ConfigFormat::XBGR2101010)
};
static ABGR16161616: &Format = &Format {
name: "abgr16161616",
vk_format: vk::Format::R16G16B16A16_UNORM,
bpp: 8,
drm: fourcc_code('A', 'B', '4', '8'),
has_alpha: true,
opaque: Some(XBGR16161616),
..default(ConfigFormat::ABGR16161616)
};
static XBGR16161616: &Format = &Format {
name: "xbgr16161616",
vk_format: vk::Format::R16G16B16A16_UNORM,
bpp: 8,
drm: fourcc_code('X', 'B', '4', '8'),
..default(ConfigFormat::XBGR16161616)
};
pub static ABGR16161616F: &Format = &Format {
name: "abgr16161616f",
vk_format: vk::Format::R16G16B16A16_SFLOAT,
bpp: 8,
drm: fourcc_code('A', 'B', '4', 'H'),
has_alpha: true,
opaque: Some(XBGR16161616F),
..default(ConfigFormat::ABGR16161616F)
};
static XBGR16161616F: &Format = &Format {
name: "xbgr16161616f",
vk_format: vk::Format::R16G16B16A16_SFLOAT,
bpp: 8,
drm: fourcc_code('X', 'B', '4', 'H'),
..default(ConfigFormat::XBGR16161616F)
};
static BGR161616: &Format = &Format {
name: "bgr161616",
vk_format: vk::Format::R16G16B16_UNORM,
bpp: 6,
drm: fourcc_code('B', 'G', '4', '8'),
..default(ConfigFormat::BGR161616)
};
static R16F: &Format = &Format {
name: "r16f",
vk_format: vk::Format::R16_SFLOAT,
bpp: 2,
drm: fourcc_code('R', ' ', ' ', 'H'),
..default(ConfigFormat::R16F)
};
static GR1616F: &Format = &Format {
name: "gr1616f",
vk_format: vk::Format::R16G16_SFLOAT,
bpp: 4,
drm: fourcc_code('G', 'R', ' ', 'H'),
..default(ConfigFormat::GR1616F)
};
static BGR161616F: &Format = &Format {
name: "bgr161616f",
vk_format: vk::Format::R16G16B16_SFLOAT,
bpp: 6,
drm: fourcc_code('B', 'G', 'R', 'H'),
..default(ConfigFormat::BGR161616F)
};
static R32F: &Format = &Format {
name: "r32f",
vk_format: vk::Format::R32_SFLOAT,
bpp: 4,
drm: fourcc_code('R', ' ', ' ', 'F'),
..default(ConfigFormat::R32F)
};
static GR3232F: &Format = &Format {
name: "gr3232f",
vk_format: vk::Format::R32G32_SFLOAT,
bpp: 8,
drm: fourcc_code('G', 'R', ' ', 'F'),
..default(ConfigFormat::GR3232F)
};
static BGR323232F: &Format = &Format {
name: "bgr323232f",
vk_format: vk::Format::R32G32B32_SFLOAT,
bpp: 12,
drm: fourcc_code('B', 'G', 'R', 'F'),
..default(ConfigFormat::BGR323232F)
};
static ABGR32323232F: &Format = &Format {
name: "abgr32323232f",
vk_format: vk::Format::R32G32B32A32_SFLOAT,
bpp: 16,
drm: fourcc_code('A', 'B', '8', 'F'),
has_alpha: true,
..default(ConfigFormat::ABGR32323232F)
};
pub static FORMATS: &[Format] = &[
*ARGB8888,
*XRGB8888,
*ABGR8888,
*XBGR8888,
*R8,
*GR88,
*RGB888,
*BGR888,
#[cfg(target_endian = "little")]
*RGBA4444,
#[cfg(target_endian = "little")]
*RGBX4444,
#[cfg(target_endian = "little")]
*BGRA4444,
#[cfg(target_endian = "little")]
*BGRX4444,
#[cfg(target_endian = "little")]
*RGB565,
#[cfg(target_endian = "little")]
*BGR565,
#[cfg(target_endian = "little")]
*RGBA5551,
#[cfg(target_endian = "little")]
*RGBX5551,
#[cfg(target_endian = "little")]
*BGRA5551,
#[cfg(target_endian = "little")]
*BGRX5551,
#[cfg(target_endian = "little")]
*ARGB1555,
#[cfg(target_endian = "little")]
*XRGB1555,
#[cfg(target_endian = "little")]
*ARGB2101010,
#[cfg(target_endian = "little")]
*XRGB2101010,
#[cfg(target_endian = "little")]
*ABGR2101010,
#[cfg(target_endian = "little")]
*XBGR2101010,
#[cfg(target_endian = "little")]
*ABGR16161616,
#[cfg(target_endian = "little")]
*XBGR16161616,
#[cfg(target_endian = "little")]
*ABGR16161616F,
#[cfg(target_endian = "little")]
*XBGR16161616F,
#[cfg(target_endian = "little")]
*BGR161616,
#[cfg(target_endian = "little")]
*R16F,
#[cfg(target_endian = "little")]
*GR1616F,
#[cfg(target_endian = "little")]
*BGR161616F,
#[cfg(target_endian = "little")]
*R32F,
#[cfg(target_endian = "little")]
*GR3232F,
#[cfg(target_endian = "little")]
*BGR323232F,
#[cfg(target_endian = "little")]
*ABGR32323232F,
];

View file

@ -0,0 +1,11 @@
[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

@ -246,7 +246,6 @@ where
dx * dx + dy * dy
}
#[expect(dead_code)]
pub fn contains_rect<U>(&self, rect: &Rect<U>) -> bool
where
U: Tag,
@ -273,12 +272,10 @@ where
self.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2
}
#[expect(dead_code)]
pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}
#[expect(dead_code)]
pub fn to_origin(&self) -> Self {
Self {
raw: RectRaw {

View file

@ -1,11 +1,5 @@
use {
crate::{
rect::{Rect, Region},
utils::{
array,
ptr_ext::{MutPtrExt, PtrExt},
},
},
crate::{Rect, Region},
jay_algorithms::rect::{
RectRaw, Tag,
region::{
@ -15,6 +9,7 @@ use {
},
smallvec::SmallVec,
std::{
array,
borrow::Cow,
cell::UnsafeCell,
fmt::{Debug, Formatter},
@ -176,7 +171,6 @@ where
}
}
#[cfg_attr(not(feature = "it"), expect(dead_code))]
pub fn extents(&self) -> Rect {
self.extents
}
@ -274,7 +268,6 @@ impl RegionBuilder {
self.base.clone()
}
#[expect(dead_code)]
pub fn clear(&mut self) {
self.pending.clear();
self.base = Region::empty();
@ -321,26 +314,26 @@ impl DamageQueue {
}
pub fn damage(&self, rects: &[Rect]) {
let datas = unsafe { self.datas.get().deref_mut() };
let datas = unsafe { &mut *self.datas.get() };
for data in datas {
data.extend(rects);
}
}
pub fn clear(&self) {
let data = unsafe { &mut self.datas.get().deref_mut()[self.this] };
let data = unsafe { &mut (&mut *self.datas.get())[self.this] };
data.clear();
}
pub fn clear_all(&self) {
let datas = unsafe { self.datas.get().deref_mut() };
let datas = unsafe { &mut *self.datas.get() };
for data in datas {
data.clear();
}
}
pub fn get(&self) -> Region {
let data = unsafe { &self.datas.get().deref()[self.this] };
let data = unsafe { &(&*self.datas.get())[self.this] };
Region::from_rects2(data)
}
}

View file

@ -1,5 +1,5 @@
use {
crate::rect::{Rect, Region},
crate::{Rect, Region},
jay_algorithms::rect::{NoTag, RectRaw},
};

View file

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

View file

@ -0,0 +1,38 @@
use {
std::{cell::Cell, error::Error, rc::Rc},
uapi::OwnedFd,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
pub enum AlphaMode {
#[default]
PremultipliedElectrical,
PremultipliedOptical,
Straight,
}
pub trait ShmMemory {
fn len(&self) -> usize;
fn safe_access(&self) -> ShmMemoryBacking;
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>>;
}
pub enum ShmMemoryBacking {
Ptr(*const [Cell<u8>]),
Fd(Rc<OwnedFd>, usize),
}
impl ShmMemory for Vec<Cell<u8>> {
fn len(&self) -> usize {
self.len()
}
fn safe_access(&self) -> ShmMemoryBacking {
ShmMemoryBacking::Ptr(&**self)
}
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
f(self);
Ok(())
}
}

Some files were not shown because too many files have changed in this diff Show more