mirror of
https://github.com/LinuxBeginnings/swww.git
synced 2026-01-12 07:21:01 -03:00
Merge pull request #410 from LGFae/waybackend
Use `waybackend` for our wayland code
This commit is contained in:
5
.github/workflows/check.yml
vendored
5
.github/workflows/check.yml
vendored
@@ -13,10 +13,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- run: sudo apt install -y libwayland-dev wayland-protocols
|
||||
- run: cargo clippy --version
|
||||
- run: cargo clippy --workspace --locked --tests
|
||||
|
||||
@@ -24,7 +25,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
8
.github/workflows/dependencies.yml
vendored
8
.github/workflows/dependencies.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-deny
|
||||
- run: cargo deny check advisories
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-deny
|
||||
- run: cargo deny check bans
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-deny
|
||||
- run: cargo deny check licenses
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-deny
|
||||
- run: cargo deny check sources
|
||||
|
||||
9
.github/workflows/test.yml
vendored
9
.github/workflows/test.yml
vendored
@@ -13,9 +13,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: taiki-e/install-action@cargo-nextest
|
||||
- run: sudo apt install -y libwayland-dev wayland-protocols
|
||||
- run: cargo build --workspace --locked --verbose
|
||||
- run: cargo nextest run --workspace --locked
|
||||
|
||||
@@ -23,9 +24,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-nextest
|
||||
- run: sudo apt install -y libwayland-dev wayland-protocols
|
||||
- run: cargo build --workspace --locked --verbose
|
||||
- run: cargo nextest run --workspace --locked
|
||||
|
||||
@@ -33,7 +35,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: SebRollen/toml-action@v1.0.2
|
||||
id: msrv
|
||||
with:
|
||||
@@ -43,5 +45,6 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ steps.msrv.outputs.value }}
|
||||
- uses: taiki-e/install-action@cargo-nextest
|
||||
- run: sudo apt install -y libwayland-dev wayland-protocols
|
||||
- run: cargo build --workspace --locked --verbose
|
||||
- run: cargo nextest run --workspace --locked
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
target/*
|
||||
completions/*
|
||||
test_images/*
|
||||
tests/test_images/*
|
||||
# Nix
|
||||
result
|
||||
|
||||
109
Cargo.lock
generated
109
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
@@ -115,9 +115,9 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.3"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
|
||||
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
@@ -177,9 +177,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.35"
|
||||
version = "4.5.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
|
||||
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -187,9 +187,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.35"
|
||||
version = "4.5.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
|
||||
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -244,7 +244,7 @@ dependencies = [
|
||||
"criterion",
|
||||
"fastrand",
|
||||
"pkg-config",
|
||||
"rustix 0.38.44",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -350,9 +350,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@@ -375,9 +375,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fast_image_resize"
|
||||
version = "5.1.2"
|
||||
version = "5.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b55264ccc579fc127eebf6c6c1841d0c160d79a44c8f6f97047b7bc4a9c0d1a5"
|
||||
checksum = "e146c782f75f50995dae9ecf9ec189fc9d0d2906318cc6826ea9451717fe52ec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"document-features",
|
||||
@@ -422,9 +422,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1"
|
||||
checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
@@ -540,15 +540,9 @@ checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
@@ -570,9 +564,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.5"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
@@ -676,6 +670,15 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
@@ -724,19 +727,6 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.5"
|
||||
@@ -746,7 +736,7 @@ dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.3",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
@@ -814,9 +804,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
@@ -828,7 +818,6 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
name = "swww"
|
||||
version = "0.9.5-masterV3"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"common",
|
||||
@@ -845,8 +834,10 @@ dependencies = [
|
||||
"keyframe",
|
||||
"libc",
|
||||
"log",
|
||||
"rustix 0.38.44",
|
||||
"rustix",
|
||||
"sd-notify",
|
||||
"waybackend",
|
||||
"waybackend-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -866,7 +857,7 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
|
||||
dependencies = [
|
||||
"rustix 1.0.5",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
@@ -876,6 +867,14 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "tests"
|
||||
version = "0.9.5-masterV3"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"image",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@@ -948,6 +947,30 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "waybackend"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76e6145c18559c8c104c6700bba2f2823c257ec6733a539674f0d54db822befb"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"log",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "waybackend-scanner"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3f4b7476ffcc25cf0df86c80cd55b2244c74078cb90fb37b0deb83becbd251e"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.8"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[workspace]
|
||||
# cargo complains that this defaults to one in virtual package manifests (for some reason)
|
||||
resolver = "2"
|
||||
members = ["client", "daemon", "common"]
|
||||
members = ["client", "daemon", "common", "tests"]
|
||||
default-members = ["client", "daemon"]
|
||||
|
||||
[workspace.package]
|
||||
@@ -9,7 +9,7 @@ version = "0.9.5-masterV3"
|
||||
authors = ["Leonardo Gibrowski Faé <leonardo.fae44@gmail.com>"]
|
||||
edition = "2021"
|
||||
license-file = "LICENSE"
|
||||
rust-version = "1.82"
|
||||
rust-version = "1.85"
|
||||
|
||||
[workspace.dependencies]
|
||||
common = { path = "common" }
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
|
||||
## Dependencies
|
||||
|
||||
- a compositor that implements:
|
||||
* wlr-layer-shell (typically wlroots based compositors)
|
||||
* xdg-output
|
||||
- a compositor that implements the wlr-layer-shell (typically wlroots based compositors)
|
||||
- [lz4](https://github.com/lz4/lz4) (for compressing frames when animating)
|
||||
|
||||
**Note that this means `swww` will not run on Gnome, because it does not implement the `wlr-layer-shell` protocol**.
|
||||
@@ -21,7 +19,8 @@
|
||||
|
||||
### Dependencies:
|
||||
|
||||
- Up to date stable rustc compiler and cargo (specifically, MSRV is 1.82.0)
|
||||
- wayland-client and wayland-protocol `.xml` files installed in your system (`pkg-config` must be able to find it)
|
||||
- Up to date stable rustc compiler and cargo (specifically, MSRV is 1.82.0)
|
||||
|
||||
To build, clone this repository and run:
|
||||
```
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
installShellFiles,
|
||||
scdoc,
|
||||
nix-gitignore,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
}: let
|
||||
version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.package.version;
|
||||
src = nix-gitignore.gitignoreSource [] ./.;
|
||||
@@ -21,6 +23,8 @@ rustPlatform.buildRustPackage {
|
||||
buildInputs = [
|
||||
lz4
|
||||
libxkbcommon
|
||||
wayland
|
||||
wayland-protocols
|
||||
];
|
||||
|
||||
doCheck = false; # Integration tests do not work in sandbox environment
|
||||
|
||||
@@ -28,14 +28,11 @@ image = { version = "0.25", default-features = false, features = [
|
||||
"tiff",
|
||||
"webp",
|
||||
] }
|
||||
fast_image_resize = "5.0"
|
||||
fast_image_resize = "5.1"
|
||||
clap = { version = "4.5", features = ["derive", "wrap_help", "env"] }
|
||||
fastrand = { version = "2.1", default-features = false, features = ["std"] }
|
||||
fastrand = { version = "2.3", default-features = false, features = ["std"] }
|
||||
common = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "4.5", features = ["derive", "env"] }
|
||||
clap_complete = "4.5"
|
||||
|
||||
@@ -10,7 +10,7 @@ license-file.workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
rustix = { version = "0.38", default-features = false, features = [
|
||||
rustix = { version = "1.0", default-features = false, features = [
|
||||
"std",
|
||||
"net",
|
||||
"shm",
|
||||
@@ -23,7 +23,7 @@ rustix = { version = "0.38", default-features = false, features = [
|
||||
pkg-config = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
fastrand = { version = "2.1", default-features = false, features = ["std"] }
|
||||
fastrand = { version = "2.3", default-features = false, features = ["std"] }
|
||||
criterion = { version = "0.5", default-features = false }
|
||||
|
||||
[[bench]]
|
||||
|
||||
@@ -75,19 +75,12 @@ pub fn get_previous_image_path(output_name: &str) -> io::Result<(String, String)
|
||||
|
||||
let mut buf = Vec::with_capacity(64);
|
||||
File::open(filepath)?.read_to_end(&mut buf)?;
|
||||
let buf = String::from_utf8(buf).map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("failed to decode bytes: {e}"),
|
||||
)
|
||||
})?;
|
||||
let buf = String::from_utf8(buf)
|
||||
.map_err(|e| std::io::Error::other(format!("failed to decode bytes: {e}")))?;
|
||||
|
||||
match buf.split_once("\n") {
|
||||
Some(buf) => Ok((buf.0.to_string(), buf.1.to_string())),
|
||||
None => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"failed to read image filter",
|
||||
)),
|
||||
None => Err(std::io::Error::other("failed to read image filter")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,9 +93,8 @@ pub fn load(output_name: &str) -> io::Result<()> {
|
||||
if let Ok(mut child) = std::process::Command::new("pidof").arg("swww").spawn() {
|
||||
if let Ok(status) = child.wait() {
|
||||
if status.success() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"there is already another swww process running".to_string(),
|
||||
return Err(std::io::Error::other(
|
||||
"there is already another swww process running",
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -181,9 +173,8 @@ fn cache_dir() -> io::Result<PathBuf> {
|
||||
create_dir(&path)?;
|
||||
Ok(path)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"failed to read both $XDG_CACHE_HOME and $HOME environment variables".to_string(),
|
||||
Err(std::io::Error::other(
|
||||
"failed to read both $XDG_CACHE_HOME and $HOME environment variables",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,11 @@ mod tests {
|
||||
for x in &mut b[..i] {
|
||||
*x = 1;
|
||||
}
|
||||
assert_eq!(unsafe { count_different(&a, &b, 0) }, (i + 2) / 3, "i: {i}");
|
||||
assert_eq!(
|
||||
unsafe { count_different(&a, &b, 0) },
|
||||
i.div_ceil(3),
|
||||
"i: {i}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ unsafe fn count_different(s1: &[u8], s2: &[u8], mut i: usize) -> usize {
|
||||
let mask = (mask & (mask >> 1) & (mask >> 2)) & 0b001001001001001;
|
||||
if mask != 0 {
|
||||
let tz = mask.trailing_zeros() as usize;
|
||||
diff += (tz + 2) / 3;
|
||||
diff += tz.div_ceil(3);
|
||||
return diff;
|
||||
}
|
||||
diff += 5;
|
||||
@@ -139,7 +139,11 @@ mod tests {
|
||||
for x in &mut b[..i] {
|
||||
*x = 1;
|
||||
}
|
||||
assert_eq!(unsafe { count_different(&a, &b, 0) }, (i + 2) / 3, "i: {i}");
|
||||
assert_eq!(
|
||||
unsafe { count_different(&a, &b, 0) },
|
||||
i.div_ceil(3),
|
||||
"i: {i}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ impl IpcSocket<Client> {
|
||||
// this will be overwriten, Rust just doesn't know it
|
||||
let mut error = Errno::INVAL;
|
||||
for _ in 0..tries {
|
||||
match net::connect_unix(&socket, &addr) {
|
||||
match net::connect(&socket, &addr) {
|
||||
Ok(()) => {
|
||||
#[cfg(debug_assertions)]
|
||||
let timeout = Duration::from_secs(30); //Some operations take a while to respond in debug mode
|
||||
@@ -140,7 +140,7 @@ impl IpcSocket<Server> {
|
||||
None,
|
||||
)
|
||||
.context(IpcErrorKind::Socket)?;
|
||||
net::bind_unix(&socket, &addr).context(IpcErrorKind::Bind)?;
|
||||
net::bind(&socket, &addr).context(IpcErrorKind::Bind)?;
|
||||
net::listen(&socket, 0).context(IpcErrorKind::Listen)?;
|
||||
Ok(Self::new(socket))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::mem::MaybeUninit;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -232,7 +233,7 @@ impl<T> IpcSocket<T> {
|
||||
let mut payload = [0u8; 16];
|
||||
payload[0..8].copy_from_slice(&msg.code.into().to_ne_bytes());
|
||||
|
||||
let mut ancillary_buf = [0u8; rustix::cmsg_space!(ScmRights(1))];
|
||||
let mut ancillary_buf = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))];
|
||||
let mut ancillary = net::SendAncillaryBuffer::new(&mut ancillary_buf);
|
||||
|
||||
let fd;
|
||||
@@ -255,7 +256,7 @@ impl<T> IpcSocket<T> {
|
||||
|
||||
pub fn recv(&self) -> Result<RawMsg, IpcError> {
|
||||
let mut buf = [0u8; 16];
|
||||
let mut ancillary_buf = [0u8; rustix::cmsg_space!(ScmRights(1))];
|
||||
let mut ancillary_buf = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))];
|
||||
|
||||
let mut control = net::RecvAncillaryBuffer::new(&mut ancillary_buf);
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ impl BgInfo {
|
||||
pixel_format,
|
||||
} = self;
|
||||
|
||||
let len = name.as_bytes().len();
|
||||
let len = name.len();
|
||||
buf[0..4].copy_from_slice(&(len as u32).to_ne_bytes());
|
||||
buf[4..4 + len].copy_from_slice(name.as_bytes());
|
||||
let mut i = 4 + len;
|
||||
@@ -278,7 +278,7 @@ impl BgInfo {
|
||||
BgImg::Img(path) => {
|
||||
buf[i] = 1;
|
||||
i += 1;
|
||||
let len = path.as_bytes().len();
|
||||
let len = path.len();
|
||||
buf[i..i + 4].copy_from_slice(&(len as u32).to_ne_bytes());
|
||||
buf[i + 4..i + 4 + len].copy_from_slice(path.as_bytes());
|
||||
i += 4 + len;
|
||||
|
||||
@@ -15,7 +15,7 @@ use rustix::mm::MapFlags;
|
||||
use rustix::mm::ProtFlags;
|
||||
use rustix::shm;
|
||||
use rustix::shm::Mode;
|
||||
use rustix::shm::ShmOFlags;
|
||||
use rustix::shm::OFlags;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mmap {
|
||||
@@ -69,13 +69,13 @@ impl Mmap {
|
||||
.map(|time| time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos())
|
||||
.map(|stamp| format!("/swww-ipc-{stamp}",));
|
||||
|
||||
let flags = ShmOFlags::CREATE | ShmOFlags::EXCL | ShmOFlags::RDWR;
|
||||
let flags = OFlags::CREATE | OFlags::EXCL | OFlags::RDWR;
|
||||
let mode = Mode::RUSR | Mode::WUSR;
|
||||
|
||||
loop {
|
||||
let filename = filenames.next().expect("infinite generator");
|
||||
match shm::shm_open(filename.as_str(), flags, mode) {
|
||||
Ok(fd) => return shm::shm_unlink(filename.as_str()).map(|()| fd),
|
||||
match shm::open(filename.as_str(), flags, mode) {
|
||||
Ok(fd) => return shm::unlink(filename.as_str()).map(|()| fd),
|
||||
Err(Errno::EXIST | Errno::INTR) => continue,
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
@@ -86,9 +86,8 @@ impl Mmap {
|
||||
fn memfd() -> io::Result<OwnedFd> {
|
||||
use rustix::fs::MemfdFlags;
|
||||
use rustix::fs::SealFlags;
|
||||
use std::ffi::CStr;
|
||||
|
||||
let name = CStr::from_bytes_with_nul(b"swww-ipc\0").unwrap();
|
||||
let name = c"swww-ipc";
|
||||
let flags = MemfdFlags::ALLOW_SEALING | MemfdFlags::CLOEXEC;
|
||||
|
||||
loop {
|
||||
|
||||
@@ -10,17 +10,18 @@ license-file.workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
common = { workspace = true }
|
||||
|
||||
waybackend = { version = "0.3" }
|
||||
log = { version = "0.4", default-features = false, features = [
|
||||
"max_level_debug",
|
||||
"release_max_level_info",
|
||||
"std",
|
||||
] }
|
||||
|
||||
rustix = { version = "0.38", default-features = false, features = ["event"] }
|
||||
rustix = { version = "1.0", default-features = false, features = ["event"] }
|
||||
libc = "0.2"
|
||||
|
||||
keyframe = "1.1"
|
||||
sd-notify = { version = "0.4" }
|
||||
|
||||
sd-notify = { version = "0.4.1" }
|
||||
|
||||
common = { workspace = true }
|
||||
[build-dependencies]
|
||||
waybackend-scanner = { version = "0.3", features = ["build-script"] }
|
||||
|
||||
33
daemon/build.rs
Normal file
33
daemon/build.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use waybackend_scanner::WaylandProtocol;
|
||||
|
||||
fn main() {
|
||||
let out_dir = std::env::var_os("OUT_DIR").expect("missing OUT_DIR environment variable");
|
||||
|
||||
let mut filepath = PathBuf::from(out_dir);
|
||||
filepath.push("wayland_protocols.rs");
|
||||
let file = std::fs::File::create(filepath).expect("failed to create wayland_protocols.rs");
|
||||
|
||||
waybackend_scanner::build_script_generate(
|
||||
&[
|
||||
WaylandProtocol::Client,
|
||||
WaylandProtocol::System(PathBuf::from_iter(&[
|
||||
"stable",
|
||||
"viewporter",
|
||||
"viewporter.xml",
|
||||
])),
|
||||
WaylandProtocol::System(PathBuf::from_iter(&[
|
||||
"staging",
|
||||
"fractional-scale",
|
||||
"fractional-scale-v1.xml",
|
||||
])),
|
||||
WaylandProtocol::Local(PathBuf::from_iter(&[
|
||||
"../",
|
||||
"protocols",
|
||||
"wlr-layer-shell-unstable-v1.xml",
|
||||
])),
|
||||
],
|
||||
&file,
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use log::error;
|
||||
use waybackend::{objman::ObjectManager, Waybackend};
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
@@ -12,7 +13,7 @@ use common::{
|
||||
mmap::MmappedBytes,
|
||||
};
|
||||
|
||||
use crate::{wallpaper::Wallpaper, wayland::ObjectManager};
|
||||
use crate::{wallpaper::Wallpaper, WaylandObject};
|
||||
|
||||
mod transitions;
|
||||
use transitions::Effect;
|
||||
@@ -70,7 +71,12 @@ impl TransitionAnimator {
|
||||
self.now = Instant::now();
|
||||
}
|
||||
|
||||
pub fn frame(&mut self, objman: &mut ObjectManager, pixel_format: PixelFormat) -> bool {
|
||||
pub fn frame(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
) -> bool {
|
||||
let Self {
|
||||
wallpapers,
|
||||
effect,
|
||||
@@ -79,8 +85,8 @@ impl TransitionAnimator {
|
||||
..
|
||||
} = self;
|
||||
if !*over {
|
||||
*over = effect.execute(objman, pixel_format, wallpapers, img.bytes());
|
||||
false
|
||||
*over = effect.execute(backend, objman, pixel_format, wallpapers, img.bytes());
|
||||
*over
|
||||
} else {
|
||||
true
|
||||
}
|
||||
@@ -122,7 +128,12 @@ impl ImageAnimator {
|
||||
self.now = Instant::now();
|
||||
}
|
||||
|
||||
pub fn frame(&mut self, objman: &mut ObjectManager, pixel_format: PixelFormat) {
|
||||
pub fn frame(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
) {
|
||||
let Self {
|
||||
wallpapers,
|
||||
animation,
|
||||
@@ -135,11 +146,12 @@ impl ImageAnimator {
|
||||
|
||||
let mut j = 0;
|
||||
while j < wallpapers.len() {
|
||||
let result = wallpapers[j]
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
decompressor.decompress(frame, canvas, pixel_format)
|
||||
});
|
||||
let result =
|
||||
wallpapers[j]
|
||||
.borrow_mut()
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
decompressor.decompress(frame, canvas, pixel_format)
|
||||
});
|
||||
|
||||
if let Err(e) = result {
|
||||
error!("failed to unpack frame: {e}");
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||
|
||||
use crate::{wallpaper::Wallpaper, wayland::ObjectManager};
|
||||
use crate::{wallpaper::Wallpaper, WaylandObject};
|
||||
use common::ipc::{PixelFormat, Transition, TransitionType};
|
||||
|
||||
use keyframe::{
|
||||
functions::BezierCurve, keyframes, mint::Vector2, num_traits::Pow, AnimationSequence,
|
||||
};
|
||||
use waybackend::{objman::ObjectManager, Waybackend};
|
||||
|
||||
fn bezier_seq(transition: &Transition, start: f32, end: f32) -> (AnimationSequence<f32>, Instant) {
|
||||
let bezier = BezierCurve::from(
|
||||
@@ -44,14 +45,17 @@ impl None {
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
) -> bool {
|
||||
wallpapers.iter().for_each(|w| {
|
||||
w.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| canvas.copy_from_slice(img))
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
canvas.copy_from_slice(img)
|
||||
})
|
||||
});
|
||||
true
|
||||
}
|
||||
@@ -83,19 +87,20 @@ impl Effect {
|
||||
|
||||
pub fn execute(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
) -> bool {
|
||||
let done = match self {
|
||||
Effect::None(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::Simple(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::Fade(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::Wave(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::Wipe(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::Grow(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::Outer(effect) => effect.run(objman, pixel_format, wallpapers, img),
|
||||
Effect::None(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
Effect::Simple(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
Effect::Fade(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
Effect::Wave(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
Effect::Wipe(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
Effect::Grow(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
Effect::Outer(effect) => effect.run(backend, objman, pixel_format, wallpapers, img),
|
||||
};
|
||||
// we only finish for real if we are doing a None or a Simple transition
|
||||
if done {
|
||||
@@ -123,7 +128,8 @@ impl Simple {
|
||||
}
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
@@ -133,7 +139,7 @@ impl Simple {
|
||||
for wallpaper in wallpapers.iter() {
|
||||
wallpaper
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
for (old, new) in canvas.iter_mut().zip(img) {
|
||||
change_byte(step, old, new);
|
||||
}
|
||||
@@ -158,7 +164,8 @@ impl Fade {
|
||||
}
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
@@ -166,7 +173,7 @@ impl Fade {
|
||||
for wallpaper in wallpapers.iter() {
|
||||
wallpaper
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
for (old, new) in canvas.iter_mut().zip(img) {
|
||||
let x = *old as u16 * (256 - self.step);
|
||||
let y = *new as u16 * self.step;
|
||||
@@ -240,7 +247,8 @@ impl Wave {
|
||||
}
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
@@ -281,7 +289,7 @@ impl Wave {
|
||||
for wallpaper in wallpapers.iter() {
|
||||
wallpaper
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
// divide in 3 sections: the one we know will not be drawn to, the one we know
|
||||
// WILL be drawn to, and the one we need to do a more expensive check on.
|
||||
// We do this by creating 2 lines: the first tangential to the wave's peaks,
|
||||
@@ -385,7 +393,8 @@ impl Wipe {
|
||||
}
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
@@ -407,7 +416,7 @@ impl Wipe {
|
||||
for wallpaper in wallpapers.iter() {
|
||||
wallpaper
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
// line formula: (x-h)*a + (y-k)*b + C = r^2
|
||||
// https://www.desmos.com/calculator/vpvzk12yar
|
||||
for line in 0..height {
|
||||
@@ -481,7 +490,8 @@ impl Grow {
|
||||
}
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
@@ -501,7 +511,7 @@ impl Grow {
|
||||
for wallpaper in wallpapers.iter() {
|
||||
wallpaper
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
let line_begin = center_y.saturating_sub(dist_center as usize);
|
||||
let line_end = height.min(center_y + dist_center as usize);
|
||||
|
||||
@@ -574,7 +584,8 @@ impl Outer {
|
||||
}
|
||||
fn run(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: &mut [Rc<RefCell<Wallpaper>>],
|
||||
img: &[u8],
|
||||
@@ -593,7 +604,7 @@ impl Outer {
|
||||
for wallpaper in wallpapers.iter() {
|
||||
wallpaper
|
||||
.borrow_mut()
|
||||
.canvas_change(objman, pixel_format, |canvas| {
|
||||
.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
// to plot half a circle with radius r, we do sqrt(r^2 - x^2)
|
||||
for line in 0..height {
|
||||
let offset = (dist_center.powi(2) - (center_y as f32 - line as f32).powi(2))
|
||||
|
||||
@@ -5,25 +5,19 @@
|
||||
mod animations;
|
||||
mod cli;
|
||||
mod wallpaper;
|
||||
#[allow(dead_code)]
|
||||
mod wayland;
|
||||
use log::{debug, error, info, warn, LevelFilter};
|
||||
use rustix::{
|
||||
event::{poll, PollFd, PollFlags},
|
||||
fd::OwnedFd,
|
||||
};
|
||||
use rustix::{fd::OwnedFd, fs::Timespec};
|
||||
|
||||
use wallpaper::Wallpaper;
|
||||
use wayland::{
|
||||
globals::{self, InitState},
|
||||
ObjectId, ObjectManager,
|
||||
};
|
||||
|
||||
use waybackend::{objman, types::ObjectId, wire, Global};
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fs,
|
||||
io::{IsTerminal, Write},
|
||||
num::{NonZeroI32, NonZeroU32},
|
||||
num::NonZeroI32,
|
||||
path::Path,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
@@ -52,57 +46,86 @@ extern "C" fn signal_handler(_s: libc::c_int) {
|
||||
}
|
||||
|
||||
struct Daemon {
|
||||
objman: ObjectManager,
|
||||
backend: waybackend::Waybackend,
|
||||
objman: objman::ObjectManager<WaylandObject>,
|
||||
registry: ObjectId,
|
||||
compositor: ObjectId,
|
||||
shm: ObjectId,
|
||||
viewporter: ObjectId,
|
||||
layer_shell: ObjectId,
|
||||
pixel_format: PixelFormat,
|
||||
wallpapers: Vec<Rc<RefCell<Wallpaper>>>,
|
||||
transition_animators: Vec<TransitionAnimator>,
|
||||
image_animators: Vec<ImageAnimator>,
|
||||
use_cache: bool,
|
||||
fractional_scale_manager: Option<ObjectId>,
|
||||
poll_time: PollTime,
|
||||
|
||||
/// We use PollTime as a way of making sure we draw at the right time.
|
||||
/// when we call `Daemon::draw` before the frame callback returned, we need to *not* draw and
|
||||
/// instead wait for the next callback, which we do with a short poll time.
|
||||
poll_time: Option<Timespec>,
|
||||
|
||||
forced_shm_format: bool,
|
||||
|
||||
// This are the globals we have received before we figured out which shm format to use
|
||||
output_globals: Option<Vec<Global>>,
|
||||
// This callback is from the sync request we make when calling `Daemon::new`
|
||||
callback: Option<ObjectId>,
|
||||
}
|
||||
|
||||
impl Daemon {
|
||||
fn new(init_state: InitState, no_cache: bool) -> Self {
|
||||
let InitState {
|
||||
output_names,
|
||||
fractional_scale,
|
||||
fn new(
|
||||
mut backend: waybackend::Waybackend,
|
||||
mut objman: objman::ObjectManager<WaylandObject>,
|
||||
shm_format: Option<PixelFormat>,
|
||||
no_cache: bool,
|
||||
output_globals: Vec<Global>,
|
||||
) -> Self {
|
||||
let registry = objman.get_first(WaylandObject::Registry).unwrap();
|
||||
let compositor = objman.get_first(WaylandObject::Compositor).unwrap();
|
||||
let shm = objman.get_first(WaylandObject::Shm).unwrap();
|
||||
let layer_shell = objman.get_first(WaylandObject::LayerShell).unwrap();
|
||||
let viewporter = objman.get_first(WaylandObject::Viewporter).unwrap();
|
||||
let fractional_scale_manager = objman.get_first(WaylandObject::LayerSurface);
|
||||
|
||||
let callback = objman.create(WaylandObject::Callback);
|
||||
wayland::wl_display::req::sync(&mut backend, waybackend::WL_DISPLAY, callback).unwrap();
|
||||
|
||||
Self {
|
||||
backend,
|
||||
objman,
|
||||
pixel_format,
|
||||
} = init_state;
|
||||
|
||||
assert_eq!(
|
||||
fractional_scale.is_some(),
|
||||
objman.fractional_scale_support()
|
||||
);
|
||||
|
||||
log::info!("Selected wl_shm format: {pixel_format:?}");
|
||||
|
||||
let mut daemon = Self {
|
||||
objman,
|
||||
pixel_format,
|
||||
registry,
|
||||
compositor,
|
||||
shm,
|
||||
viewporter,
|
||||
layer_shell,
|
||||
pixel_format: shm_format.unwrap_or(PixelFormat::Xrgb),
|
||||
wallpapers: Vec::new(),
|
||||
transition_animators: Vec::new(),
|
||||
image_animators: Vec::new(),
|
||||
use_cache: !no_cache,
|
||||
fractional_scale_manager: fractional_scale.map(|x| x.id()),
|
||||
poll_time: PollTime::Never,
|
||||
};
|
||||
|
||||
for output_name in output_names {
|
||||
daemon.new_output(output_name);
|
||||
fractional_scale_manager,
|
||||
poll_time: None,
|
||||
forced_shm_format: shm_format.is_some(),
|
||||
output_globals: Some(output_globals),
|
||||
callback: Some(callback),
|
||||
}
|
||||
}
|
||||
|
||||
daemon
|
||||
/// always sets the poll time to the smalest value
|
||||
fn set_poll_time(&mut self, new_time: Timespec) {
|
||||
match self.poll_time {
|
||||
None => self.poll_time = Some(new_time),
|
||||
Some(t1) => {
|
||||
if new_time < t1 {
|
||||
self.poll_time = Some(new_time)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_output(&mut self, output_name: u32) {
|
||||
let wallpaper = Rc::new(RefCell::new(Wallpaper::new(
|
||||
&mut self.objman,
|
||||
self.pixel_format,
|
||||
self.fractional_scale_manager,
|
||||
output_name,
|
||||
)));
|
||||
let wallpaper = Rc::new(RefCell::new(Wallpaper::new(self, output_name)));
|
||||
self.wallpapers.push(wallpaper);
|
||||
}
|
||||
|
||||
@@ -123,10 +146,19 @@ impl Daemon {
|
||||
for wallpaper in &wallpapers {
|
||||
let mut wallpaper = wallpaper.borrow_mut();
|
||||
wallpaper.set_img_info(common::ipc::BgImg::Color(clear.color));
|
||||
wallpaper.clear(&mut self.objman, self.pixel_format, clear.color);
|
||||
wallpaper.clear(
|
||||
&mut self.backend,
|
||||
&mut self.objman,
|
||||
self.pixel_format,
|
||||
clear.color,
|
||||
);
|
||||
}
|
||||
crate::wallpaper::attach_buffers_and_damage_surfaces(&mut self.objman, &wallpapers);
|
||||
crate::wallpaper::commit_wallpapers(&wallpapers);
|
||||
crate::wallpaper::attach_buffers_and_damage_surfaces(
|
||||
&mut self.backend,
|
||||
&mut self.objman,
|
||||
&wallpapers,
|
||||
);
|
||||
crate::wallpaper::commit_wallpapers(&mut self.backend, &wallpapers);
|
||||
Answer::Ok
|
||||
}
|
||||
RequestRecv::Ping => Answer::Ping(self.wallpapers.iter().all(|w| {
|
||||
@@ -162,11 +194,14 @@ impl Daemon {
|
||||
img,
|
||||
animation,
|
||||
) {
|
||||
transition.frame(&mut self.objman, self.pixel_format);
|
||||
transition.frame(&mut self.backend, &mut self.objman, self.pixel_format);
|
||||
self.transition_animators.push(transition);
|
||||
}
|
||||
}
|
||||
self.poll_time = PollTime::Instant;
|
||||
self.set_poll_time(Timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
});
|
||||
Answer::Ok
|
||||
}
|
||||
};
|
||||
@@ -195,7 +230,7 @@ impl Daemon {
|
||||
}
|
||||
|
||||
fn draw(&mut self) {
|
||||
self.poll_time = PollTime::Never;
|
||||
self.poll_time = None;
|
||||
|
||||
let mut i = 0;
|
||||
while i < self.transition_animators.len() {
|
||||
@@ -206,8 +241,11 @@ impl Daemon {
|
||||
.all(|w| w.borrow().is_draw_ready())
|
||||
{
|
||||
let time = animator.time_to_draw();
|
||||
if time > Duration::from_micros(1200) {
|
||||
self.poll_time = PollTime::Short;
|
||||
if time > Duration::from_micros(1000) {
|
||||
self.set_poll_time(Timespec {
|
||||
tv_sec: time.as_secs() as i64,
|
||||
tv_nsec: time.subsec_nanos().saturating_sub(500_000) as i64,
|
||||
});
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
@@ -217,12 +255,14 @@ impl Daemon {
|
||||
}
|
||||
|
||||
wallpaper::attach_buffers_and_damage_surfaces(
|
||||
&mut self.backend,
|
||||
&mut self.objman,
|
||||
&animator.wallpapers,
|
||||
);
|
||||
wallpaper::commit_wallpapers(&animator.wallpapers);
|
||||
|
||||
wallpaper::commit_wallpapers(&mut self.backend, &animator.wallpapers);
|
||||
animator.updt_time();
|
||||
if animator.frame(&mut self.objman, self.pixel_format) {
|
||||
if animator.frame(&mut self.backend, &mut self.objman, self.pixel_format) {
|
||||
let animator = self.transition_animators.swap_remove(i);
|
||||
if let Some(anim) = animator.into_image_animator() {
|
||||
self.image_animators.push(anim);
|
||||
@@ -230,19 +270,30 @@ impl Daemon {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let time = animator.time_to_draw();
|
||||
self.set_poll_time(Timespec {
|
||||
tv_sec: time.as_secs() as i64,
|
||||
tv_nsec: time.subsec_nanos().saturating_sub(500_000) as i64,
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
|
||||
self.image_animators.retain(|a| !a.wallpapers.is_empty());
|
||||
for animator in &mut self.image_animators {
|
||||
let mut i = 0;
|
||||
while i < self.image_animators.len() {
|
||||
let animator = &mut self.image_animators[i];
|
||||
if animator
|
||||
.wallpapers
|
||||
.iter()
|
||||
.all(|w| w.borrow().is_draw_ready())
|
||||
{
|
||||
let time = animator.time_to_draw();
|
||||
if time > Duration::from_micros(1200) {
|
||||
self.poll_time = PollTime::Short;
|
||||
if time > Duration::from_micros(1000) {
|
||||
self.set_poll_time(Timespec {
|
||||
tv_sec: time.as_secs() as i64,
|
||||
tv_nsec: time.subsec_nanos().saturating_sub(500_000) as i64,
|
||||
});
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -251,13 +302,20 @@ impl Daemon {
|
||||
}
|
||||
|
||||
wallpaper::attach_buffers_and_damage_surfaces(
|
||||
&mut self.backend,
|
||||
&mut self.objman,
|
||||
&animator.wallpapers,
|
||||
);
|
||||
wallpaper::commit_wallpapers(&animator.wallpapers);
|
||||
wallpaper::commit_wallpapers(&mut self.backend, &animator.wallpapers);
|
||||
animator.updt_time();
|
||||
animator.frame(&mut self.objman, self.pixel_format);
|
||||
animator.frame(&mut self.backend, &mut self.objman, self.pixel_format);
|
||||
}
|
||||
let time = animator.time_to_draw();
|
||||
self.set_poll_time(Timespec {
|
||||
tv_sec: time.as_secs() as i64,
|
||||
tv_nsec: time.subsec_nanos().saturating_sub(500_000) as i64,
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,22 +339,21 @@ impl Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_display::HasObjman for Daemon {
|
||||
fn objman(&mut self) -> &mut ObjectManager {
|
||||
&mut self.objman
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_display::EvHandler for Daemon {
|
||||
fn delete_id(&mut self, id: u32) {
|
||||
if let Some(id) = NonZeroU32::new(id) {
|
||||
self.objman.remove(ObjectId::new(id));
|
||||
impl wayland::wl_display::EvHandler for Daemon {
|
||||
fn delete_id(&mut self, _: ObjectId, id: u32) {
|
||||
if let Ok(id) = ObjectId::try_new(id) {
|
||||
self.objman.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, _: ObjectId, object_id: ObjectId, code: u32, message: &str) {
|
||||
error!("WAYLAND PROTOCOL ERROR: object: {object_id}, code: {code}, message: {message}");
|
||||
exit_daemon();
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_registry::EvHandler for Daemon {
|
||||
fn global(&mut self, name: u32, interface: &str, version: u32) {
|
||||
impl wayland::wl_registry::EvHandler for Daemon {
|
||||
fn global(&mut self, _: ObjectId, name: u32, interface: &str, version: u32) {
|
||||
if interface == "wl_output" {
|
||||
if version < 4 {
|
||||
error!("your compositor must support at least version 4 of wl_output");
|
||||
@@ -306,27 +363,48 @@ impl wayland::interfaces::wl_registry::EvHandler for Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
fn global_remove(&mut self, name: u32) {
|
||||
fn global_remove(&mut self, _: ObjectId, name: u32) {
|
||||
if let Some(i) = self
|
||||
.wallpapers
|
||||
.iter()
|
||||
.position(|w| w.borrow().has_output_name(name))
|
||||
{
|
||||
let w = self.wallpapers.remove(i);
|
||||
w.borrow_mut().destroy(&mut self.backend);
|
||||
self.stop_animations(&[w]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_shm::EvHandler for Daemon {
|
||||
fn format(&mut self, format: u32) {
|
||||
warn!(
|
||||
"received a wl_shm format after initialization: {format}. This shouldn't be possible"
|
||||
);
|
||||
impl wayland::wl_shm::EvHandler for Daemon {
|
||||
fn format(&mut self, _: ObjectId, format: wayland::wl_shm::Format) {
|
||||
use wayland::wl_shm::Format;
|
||||
match format {
|
||||
Format::xrgb8888 => debug!("available shm format: Xrbg"),
|
||||
Format::xbgr8888 => {
|
||||
debug!("available shm format: Xbgr");
|
||||
if !self.forced_shm_format && self.pixel_format == PixelFormat::Xrgb {
|
||||
self.pixel_format = PixelFormat::Xbgr;
|
||||
}
|
||||
}
|
||||
Format::rgb888 => {
|
||||
debug!("available shm format: Rbg");
|
||||
if !self.forced_shm_format && self.pixel_format != PixelFormat::Bgr {
|
||||
self.pixel_format = PixelFormat::Rgb
|
||||
}
|
||||
}
|
||||
Format::bgr888 => {
|
||||
debug!("available shm format: Bgr");
|
||||
if !self.forced_shm_format {
|
||||
self.pixel_format = PixelFormat::Bgr
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_output::EvHandler for Daemon {
|
||||
impl wayland::wl_output::EvHandler for Daemon {
|
||||
fn geometry(
|
||||
&mut self,
|
||||
sender_id: ObjectId,
|
||||
@@ -334,25 +412,32 @@ impl wayland::interfaces::wl_output::EvHandler for Daemon {
|
||||
_y: i32,
|
||||
_physical_width: i32,
|
||||
_physical_height: i32,
|
||||
_subpixel: i32,
|
||||
_subpixel: wayland::wl_output::Subpixel,
|
||||
_make: &str,
|
||||
_model: &str,
|
||||
transform: i32,
|
||||
transform: wayland::wl_output::Transform,
|
||||
) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
let mut wallpaper = wallpaper.borrow_mut();
|
||||
if wallpaper.has_output(sender_id) {
|
||||
if transform as u32 > wayland::interfaces::wl_output::transform::FLIPPED_270 {
|
||||
error!("received invalid transform value from compositor: {transform}")
|
||||
if transform == wayland::wl_output::Transform::flipped_270 {
|
||||
error!("received invalid transform value from compositor: {transform:?}")
|
||||
} else {
|
||||
wallpaper.set_transform(transform as u32);
|
||||
wallpaper.set_transform(transform);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mode(&mut self, sender_id: ObjectId, _flags: u32, width: i32, height: i32, _refresh: i32) {
|
||||
fn mode(
|
||||
&mut self,
|
||||
sender_id: ObjectId,
|
||||
_flags: wayland::wl_output::Mode,
|
||||
width: i32,
|
||||
height: i32,
|
||||
_refresh: i32,
|
||||
) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
let mut wallpaper = wallpaper.borrow_mut();
|
||||
if wallpaper.has_output(sender_id) {
|
||||
@@ -365,10 +450,11 @@ impl wayland::interfaces::wl_output::EvHandler for Daemon {
|
||||
fn done(&mut self, sender_id: ObjectId) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
if wallpaper.borrow().has_output(sender_id) {
|
||||
if wallpaper
|
||||
.borrow_mut()
|
||||
.commit_surface_changes(&mut self.objman, self.use_cache)
|
||||
{
|
||||
if wallpaper.borrow_mut().commit_surface_changes(
|
||||
&mut self.backend,
|
||||
&mut self.objman,
|
||||
self.use_cache,
|
||||
) {
|
||||
self.stop_animations(&[wallpaper.clone()]);
|
||||
}
|
||||
break;
|
||||
@@ -410,7 +496,7 @@ impl wayland::interfaces::wl_output::EvHandler for Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_surface::EvHandler for Daemon {
|
||||
impl wayland::wl_surface::EvHandler for Daemon {
|
||||
fn enter(&mut self, _sender_id: ObjectId, output: ObjectId) {
|
||||
debug!("Output {}: Surface Enter", output.get());
|
||||
}
|
||||
@@ -432,43 +518,70 @@ impl wayland::interfaces::wl_surface::EvHandler for Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
fn preferred_buffer_transform(&mut self, _sender_id: ObjectId, _transform: u32) {
|
||||
fn preferred_buffer_transform(
|
||||
&mut self,
|
||||
_sender_id: ObjectId,
|
||||
_transform: wayland::wl_output::Transform,
|
||||
) {
|
||||
warn!("Received PreferredBufferTransform. We currently ignore those")
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_buffer::EvHandler for Daemon {
|
||||
impl wayland::wl_region::EvHandler for Daemon {}
|
||||
|
||||
impl wayland::wl_buffer::EvHandler for Daemon {
|
||||
fn release(&mut self, sender_id: ObjectId) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
let strong_count = Rc::strong_count(wallpaper);
|
||||
if wallpaper
|
||||
.borrow_mut()
|
||||
.try_set_buffer_release_flag(sender_id, strong_count)
|
||||
{
|
||||
break;
|
||||
if wallpaper.borrow_mut().try_set_buffer_release_flag(
|
||||
&mut self.backend,
|
||||
sender_id,
|
||||
strong_count,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
error!("We failed to find wayland buffer with id: {sender_id}. This should be impossible.");
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wl_callback::EvHandler for Daemon {
|
||||
impl wayland::wl_callback::EvHandler for Daemon {
|
||||
fn done(&mut self, sender_id: ObjectId, _callback_data: u32) {
|
||||
if self.callback.is_some_and(|obj| obj == sender_id) {
|
||||
info!("selected pixel format: {:?}", self.pixel_format);
|
||||
|
||||
let output_globals = self.output_globals.take();
|
||||
for output in output_globals.unwrap() {
|
||||
self.new_output(output.name());
|
||||
}
|
||||
self.callback = None;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
if wallpaper.borrow().has_callback(sender_id) {
|
||||
wallpaper.borrow_mut().frame_callback_completed();
|
||||
self.draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::zwlr_layer_surface_v1::EvHandler for Daemon {
|
||||
impl wayland::wl_compositor::EvHandler for Daemon {}
|
||||
impl wayland::wl_shm_pool::EvHandler for Daemon {}
|
||||
|
||||
impl wayland::zwlr_layer_shell_v1::EvHandler for Daemon {}
|
||||
impl wayland::zwlr_layer_surface_v1::EvHandler for Daemon {
|
||||
fn configure(&mut self, sender_id: ObjectId, serial: u32, _width: u32, _height: u32) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
if wallpaper.borrow().has_layer_surface(sender_id) {
|
||||
wayland::interfaces::zwlr_layer_surface_v1::req::ack_configure(sender_id, serial)
|
||||
.unwrap();
|
||||
wayland::zwlr_layer_surface_v1::req::ack_configure(
|
||||
&mut self.backend,
|
||||
sender_id,
|
||||
serial,
|
||||
)
|
||||
.unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -481,22 +594,24 @@ impl wayland::interfaces::zwlr_layer_surface_v1::EvHandler for Daemon {
|
||||
.position(|w| w.borrow().has_layer_surface(sender_id))
|
||||
{
|
||||
let w = self.wallpapers.remove(i);
|
||||
w.borrow_mut().destroy(&mut self.backend);
|
||||
self.stop_animations(&[w]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland::interfaces::wp_fractional_scale_v1::EvHandler for Daemon {
|
||||
impl wayland::wp_fractional_scale_v1::EvHandler for Daemon {
|
||||
fn preferred_scale(&mut self, sender_id: ObjectId, scale: u32) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
if wallpaper.borrow().has_fractional_scale(sender_id) {
|
||||
match NonZeroI32::new(scale as i32) {
|
||||
Some(factor) => {
|
||||
wallpaper.borrow_mut().set_scale(Scale::Fractional(factor));
|
||||
if wallpaper
|
||||
.borrow_mut()
|
||||
.commit_surface_changes(&mut self.objman, self.use_cache)
|
||||
{
|
||||
if wallpaper.borrow_mut().commit_surface_changes(
|
||||
&mut self.backend,
|
||||
&mut self.objman,
|
||||
self.use_cache,
|
||||
) {
|
||||
self.stop_animations(&[wallpaper.clone()]);
|
||||
}
|
||||
}
|
||||
@@ -508,13 +623,87 @@ impl wayland::interfaces::wp_fractional_scale_v1::EvHandler for Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
impl wayland::wp_viewporter::EvHandler for Daemon {}
|
||||
impl wayland::wp_viewport::EvHandler for Daemon {}
|
||||
impl wayland::wp_fractional_scale_manager_v1::EvHandler for Daemon {}
|
||||
|
||||
impl Drop for Daemon {
|
||||
fn drop(&mut self) {
|
||||
for wallpaper in self.wallpapers.iter() {
|
||||
let mut w = wallpaper.borrow_mut();
|
||||
w.destroy(&mut self.backend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum WaylandObject {
|
||||
// standard stuff
|
||||
Display,
|
||||
Registry,
|
||||
Callback,
|
||||
Compositor,
|
||||
Shm,
|
||||
ShmPool,
|
||||
Buffer,
|
||||
Surface,
|
||||
Region,
|
||||
Output,
|
||||
|
||||
// layer shell
|
||||
LayerShell,
|
||||
LayerSurface,
|
||||
|
||||
// Viewporter
|
||||
Viewporter,
|
||||
Viewport,
|
||||
|
||||
// Fractional Scaling
|
||||
FractionalScaler,
|
||||
FractionalScale,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// first, get the command line arguments and make the logger
|
||||
let cli = cli::Cli::new();
|
||||
make_logger(cli.quiet);
|
||||
|
||||
// initialize the wayland connection, getting all the necessary globals
|
||||
let init_state = wayland::globals::init(cli.format);
|
||||
// next, initialize all wayland stuff
|
||||
let mut backend = waybackend::connect()?;
|
||||
let mut receiver = wire::Receiver::new();
|
||||
let mut objman = objman::ObjectManager::<WaylandObject>::new(WaylandObject::Display);
|
||||
let registry = objman.create(WaylandObject::Registry);
|
||||
let callback = objman.create(WaylandObject::Callback);
|
||||
let (mut globals, delete_callback) =
|
||||
waybackend::roundtrip(&mut backend, &mut receiver, registry, callback)?;
|
||||
|
||||
if delete_callback {
|
||||
objman.remove(callback);
|
||||
}
|
||||
|
||||
// macro to help binding the globals
|
||||
macro_rules! match_global {
|
||||
($global:ident, $(($interface:ident, $object:path)),*$(,)?) => {
|
||||
match $global.interface() {
|
||||
$($interface::NAME => $global.bind(&mut backend, registry, &mut objman, $object)?),*,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for global in &globals {
|
||||
use wayland::*;
|
||||
use WaylandObject::*;
|
||||
match_global!(
|
||||
global,
|
||||
(wl_compositor, Compositor),
|
||||
(wl_shm, Shm),
|
||||
(zwlr_layer_shell_v1, LayerShell),
|
||||
(wp_viewporter, Viewporter),
|
||||
(wp_fractional_scale_manager_v1, FractionalScaler),
|
||||
);
|
||||
}
|
||||
globals.retain(|global| global.interface() == wayland::wl_output::NAME);
|
||||
|
||||
// create the socket listener and setup the signal handlers
|
||||
// this will also return an error if there is an `swww-daemon` instance already
|
||||
@@ -523,7 +712,7 @@ fn main() -> Result<(), String> {
|
||||
setup_signals();
|
||||
|
||||
// use the initializer to create the Daemon, then drop it to free up the memory
|
||||
let mut daemon = Daemon::new(init_state, cli.no_cache);
|
||||
let mut daemon = Daemon::new(backend, objman, cli.format, cli.no_cache, globals);
|
||||
|
||||
if let Ok(true) = sd_notify::booted() {
|
||||
if let Err(e) = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) {
|
||||
@@ -531,68 +720,85 @@ fn main() -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
let wayland_fd = wayland::globals::wayland_fd();
|
||||
let mut fds = [
|
||||
PollFd::new(&wayland_fd, PollFlags::IN),
|
||||
PollFd::new(&listener.0, PollFlags::IN),
|
||||
];
|
||||
// dispatch macro
|
||||
macro_rules! match_enum_with_interface {
|
||||
($handler:ident, $object:ident, $msg:ident, $(($variant:path, $interface:ident)),*$(,)?) => {
|
||||
match $object {
|
||||
$($variant => $interface::event(&mut $handler, &mut $msg)?),*,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// main loop
|
||||
while !should_daemon_exit() {
|
||||
use wayland::{interfaces::*, wire, WlDynObj};
|
||||
use rustix::event::{PollFd, PollFlags};
|
||||
use wayland::*;
|
||||
use WaylandObject::*;
|
||||
|
||||
if let Err(e) = poll(&mut fds, daemon.poll_time.into()) {
|
||||
match e {
|
||||
rustix::io::Errno::INTR => continue,
|
||||
_ => return Err(format!("failed to poll file descriptors: {e:?}")),
|
||||
}
|
||||
daemon.backend.flush()?;
|
||||
|
||||
let mut fds = [
|
||||
PollFd::new(&daemon.backend.wayland_fd, PollFlags::IN),
|
||||
PollFd::new(&listener.0, PollFlags::IN),
|
||||
];
|
||||
|
||||
// Note: we cannot use rustix::io::retry_on_intr because it makes CTRL-C fail on the
|
||||
// terminal
|
||||
match rustix::event::poll(&mut fds, daemon.poll_time.as_ref()) {
|
||||
Ok(_) => (),
|
||||
Err(rustix::io::Errno::INTR | rustix::io::Errno::WOULDBLOCK) => continue,
|
||||
Err(e) => return Err(Box::new(e)),
|
||||
}
|
||||
|
||||
if !fds[0].revents().is_empty() {
|
||||
let (msg, payload) = match wire::WireMsg::recv() {
|
||||
Ok((msg, payload)) => (msg, payload),
|
||||
Err(rustix::io::Errno::INTR) => continue,
|
||||
Err(e) => return Err(format!("failed to receive wire message: {e:?}")),
|
||||
};
|
||||
let wayland_event = !fds[0].revents().is_empty();
|
||||
let socket_event = !fds[1].revents().is_empty();
|
||||
|
||||
match msg.sender_id() {
|
||||
globals::WL_DISPLAY => wl_display::event(&mut daemon, msg, payload),
|
||||
globals::WL_REGISTRY => wl_registry::event(&mut daemon, msg, payload),
|
||||
globals::WL_COMPOSITOR => error!("wl_compositor has no events"),
|
||||
globals::WL_SHM => wl_shm::event(&mut daemon, msg, payload),
|
||||
globals::WP_VIEWPORTER => error!("wp_viewporter has no events"),
|
||||
globals::ZWLR_LAYER_SHELL_V1 => error!("zwlr_layer_shell_v1 has no events"),
|
||||
other => {
|
||||
let obj_id = daemon.objman.get(other);
|
||||
match obj_id {
|
||||
Some(WlDynObj::Output) => wl_output::event(&mut daemon, msg, payload),
|
||||
Some(WlDynObj::Surface) => wl_surface::event(&mut daemon, msg, payload),
|
||||
Some(WlDynObj::Region) => error!("wl_region has no events"),
|
||||
Some(WlDynObj::LayerSurface) => {
|
||||
zwlr_layer_surface_v1::event(&mut daemon, msg, payload)
|
||||
}
|
||||
Some(WlDynObj::Buffer) => wl_buffer::event(&mut daemon, msg, payload),
|
||||
Some(WlDynObj::ShmPool) => error!("wl_shm_pool has no events"),
|
||||
Some(WlDynObj::Callback) => wl_callback::event(&mut daemon, msg, payload),
|
||||
Some(WlDynObj::Viewport) => error!("wp_viewport has no events"),
|
||||
Some(WlDynObj::FractionalScale) => {
|
||||
wp_fractional_scale_v1::event(&mut daemon, msg, payload)
|
||||
}
|
||||
None => error!("Received event for deleted object ({other:?})"),
|
||||
}
|
||||
if wayland_event {
|
||||
let mut msg = receiver.recv(&daemon.backend.wayland_fd)?;
|
||||
while msg.has_next()? {
|
||||
let sender_id = msg.sender_id();
|
||||
if sender_id == waybackend::WL_DISPLAY {
|
||||
wl_display::event(&mut daemon, &mut msg)?;
|
||||
} else {
|
||||
let sender = daemon
|
||||
.objman
|
||||
.get(sender_id)
|
||||
.expect("received wayland message from unknown object");
|
||||
match_enum_with_interface!(
|
||||
daemon,
|
||||
sender,
|
||||
msg,
|
||||
(Display, wl_display),
|
||||
(Registry, wl_registry),
|
||||
(Callback, wl_callback),
|
||||
(Compositor, wl_compositor),
|
||||
(Shm, wl_shm),
|
||||
(ShmPool, wl_shm_pool),
|
||||
(Buffer, wl_buffer),
|
||||
(Surface, wl_surface),
|
||||
(Region, wl_region),
|
||||
(Output, wl_output),
|
||||
(LayerShell, zwlr_layer_shell_v1),
|
||||
(LayerSurface, zwlr_layer_surface_v1),
|
||||
(Viewporter, wp_viewporter),
|
||||
(Viewport, wp_viewport),
|
||||
(FractionalScaler, wp_fractional_scale_manager_v1),
|
||||
(FractionalScale, wp_fractional_scale_v1),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !fds[1].revents().is_empty() {
|
||||
if socket_event {
|
||||
// See above note about rustix::retry_on_intr
|
||||
match rustix::net::accept(&listener.0) {
|
||||
Ok(stream) => daemon.recv_socket_msg(IpcSocket::new(stream)),
|
||||
Err(rustix::io::Errno::INTR | rustix::io::Errno::WOULDBLOCK) => continue,
|
||||
Err(e) => return Err(format!("failed to accept incoming connection: {e}")),
|
||||
Err(e) => return Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
if !matches!(daemon.poll_time, PollTime::Never) {
|
||||
if daemon.poll_time.is_some() {
|
||||
daemon.draw();
|
||||
}
|
||||
}
|
||||
@@ -679,26 +885,6 @@ impl Drop for SocketWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Clone, Copy)]
|
||||
/// We use PollTime as a way of making sure we draw at the right time
|
||||
/// when we call `Daemon::draw` before the frame callback returned, we need to *not* draw and
|
||||
/// instead wait for the next callback, which we do with a short poll time.
|
||||
///
|
||||
/// The instant poll time is for when we receive an img request, after we set up the requested
|
||||
/// transitions
|
||||
enum PollTime {
|
||||
Never = -1,
|
||||
Instant = 0,
|
||||
Short = 1,
|
||||
}
|
||||
|
||||
impl From<PollTime> for i32 {
|
||||
fn from(value: PollTime) -> Self {
|
||||
value as i32
|
||||
}
|
||||
}
|
||||
|
||||
struct Logger {
|
||||
level_filter: LevelFilter,
|
||||
start: std::time::Instant,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use common::ipc::{BgImg, BgInfo, PixelFormat, Scale};
|
||||
use log::{debug, error, warn};
|
||||
use waybackend::{objman::ObjectManager, types::ObjectId, Waybackend};
|
||||
|
||||
use std::{cell::RefCell, num::NonZeroI32, rc::Rc, sync::atomic::AtomicBool};
|
||||
|
||||
use crate::wayland::{
|
||||
bump_pool::BumpPool,
|
||||
interfaces::{
|
||||
wl_output, wl_surface, wp_fractional_scale_v1, wp_viewport, zwlr_layer_surface_v1,
|
||||
use crate::{
|
||||
wayland::{
|
||||
bump_pool::BumpPool, wl_compositor, wl_output, wl_region, wl_registry, wl_surface,
|
||||
wp_fractional_scale_manager_v1, wp_fractional_scale_v1, wp_viewport, wp_viewporter,
|
||||
zwlr_layer_shell_v1, zwlr_layer_surface_v1,
|
||||
},
|
||||
ObjectId, ObjectManager, WlDynObj,
|
||||
WaylandObject,
|
||||
};
|
||||
|
||||
struct FrameCallbackHandler {
|
||||
@@ -17,31 +19,40 @@ struct FrameCallbackHandler {
|
||||
}
|
||||
|
||||
impl FrameCallbackHandler {
|
||||
fn new(objman: &mut ObjectManager, surface: ObjectId) -> Self {
|
||||
let callback = objman.create(WlDynObj::Callback);
|
||||
wl_surface::req::frame(surface, callback).unwrap();
|
||||
fn new(
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
surface: ObjectId,
|
||||
) -> Self {
|
||||
let callback = objman.create(WaylandObject::Callback);
|
||||
wl_surface::req::frame(backend, surface, callback).unwrap();
|
||||
FrameCallbackHandler {
|
||||
done: true, // we do not have to wait for the first frame
|
||||
callback,
|
||||
}
|
||||
}
|
||||
|
||||
fn request_frame_callback(&mut self, objman: &mut ObjectManager, surface: ObjectId) {
|
||||
let callback = objman.create(WlDynObj::Callback);
|
||||
wl_surface::req::frame(surface, callback).unwrap();
|
||||
fn request_frame_callback(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
surface: ObjectId,
|
||||
) {
|
||||
let callback = objman.create(WaylandObject::Callback);
|
||||
wl_surface::req::frame(backend, surface, callback).unwrap();
|
||||
self.callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/// Owns all the necessary information for drawing.
|
||||
#[derive(Clone, Debug)]
|
||||
struct WallpaperInner {
|
||||
name: Option<String>,
|
||||
desc: Option<String>,
|
||||
pub struct WallpaperInner {
|
||||
pub name: Option<String>,
|
||||
pub desc: Option<String>,
|
||||
width: NonZeroI32,
|
||||
height: NonZeroI32,
|
||||
scale_factor: Scale,
|
||||
transform: u32,
|
||||
transform: wl_output::Transform,
|
||||
}
|
||||
|
||||
impl Default for WallpaperInner {
|
||||
@@ -52,7 +63,7 @@ impl Default for WallpaperInner {
|
||||
width: unsafe { NonZeroI32::new_unchecked(4) },
|
||||
height: unsafe { NonZeroI32::new_unchecked(4) },
|
||||
scale_factor: Scale::Whole(unsafe { NonZeroI32::new_unchecked(1) }),
|
||||
transform: wl_output::transform::NORMAL,
|
||||
transform: wl_output::Transform::normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,13 +71,12 @@ impl Default for WallpaperInner {
|
||||
pub(super) struct Wallpaper {
|
||||
output: ObjectId,
|
||||
output_name: u32,
|
||||
wl_surface: ObjectId,
|
||||
wp_viewport: ObjectId,
|
||||
#[allow(unused)]
|
||||
wp_fractional: Option<ObjectId>,
|
||||
layer_surface: ObjectId,
|
||||
pub wl_surface: ObjectId,
|
||||
pub wp_viewport: ObjectId,
|
||||
pub wp_fractional: Option<ObjectId>,
|
||||
pub layer_surface: ObjectId,
|
||||
|
||||
inner: WallpaperInner,
|
||||
pub inner: WallpaperInner,
|
||||
inner_staging: WallpaperInner,
|
||||
|
||||
pub configured: AtomicBool,
|
||||
@@ -83,42 +93,50 @@ impl std::cmp::PartialEq for Wallpaper {
|
||||
}
|
||||
|
||||
impl Wallpaper {
|
||||
pub(crate) fn new(
|
||||
objman: &mut ObjectManager,
|
||||
pixel_format: PixelFormat,
|
||||
fractional_scale_manager: Option<ObjectId>,
|
||||
output_name: u32,
|
||||
) -> Self {
|
||||
use crate::wayland::{self, interfaces::*};
|
||||
let output = objman.create(wayland::WlDynObj::Output);
|
||||
wl_registry::req::bind(output_name, output, "wl_output", 4).unwrap();
|
||||
pub(crate) fn new(daemon: &mut crate::Daemon, output_name: u32) -> Self {
|
||||
let crate::Daemon {
|
||||
objman,
|
||||
backend,
|
||||
pixel_format,
|
||||
registry,
|
||||
compositor,
|
||||
shm,
|
||||
viewporter,
|
||||
fractional_scale_manager,
|
||||
layer_shell,
|
||||
..
|
||||
} = daemon;
|
||||
let output = objman.create(WaylandObject::Output);
|
||||
wl_registry::req::bind(backend, *registry, output_name, output, "wl_output", 4).unwrap();
|
||||
|
||||
let wl_surface = objman.create(wayland::WlDynObj::Surface);
|
||||
wl_compositor::req::create_surface(wl_surface).unwrap();
|
||||
let wl_surface = objman.create(WaylandObject::Surface);
|
||||
wl_compositor::req::create_surface(backend, *compositor, wl_surface).unwrap();
|
||||
|
||||
let region = objman.create(wayland::WlDynObj::Region);
|
||||
wl_compositor::req::create_region(region).unwrap();
|
||||
let region = objman.create(WaylandObject::Region);
|
||||
wl_compositor::req::create_region(backend, *compositor, region).unwrap();
|
||||
|
||||
wl_surface::req::set_input_region(wl_surface, Some(region)).unwrap();
|
||||
wl_region::req::destroy(region).unwrap();
|
||||
wl_surface::req::set_input_region(backend, wl_surface, Some(region)).unwrap();
|
||||
wl_region::req::destroy(backend, region).unwrap();
|
||||
|
||||
let layer_surface = objman.create(wayland::WlDynObj::LayerSurface);
|
||||
let layer_surface = objman.create(WaylandObject::LayerSurface);
|
||||
zwlr_layer_shell_v1::req::get_layer_surface(
|
||||
backend,
|
||||
*layer_shell,
|
||||
layer_surface,
|
||||
wl_surface,
|
||||
Some(output),
|
||||
zwlr_layer_shell_v1::layer::BACKGROUND,
|
||||
zwlr_layer_shell_v1::Layer::background,
|
||||
"swww-daemon",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let wp_viewport = objman.create(wayland::WlDynObj::Viewport);
|
||||
wp_viewporter::req::get_viewport(wp_viewport, wl_surface).unwrap();
|
||||
let wp_viewport = objman.create(WaylandObject::Viewport);
|
||||
wp_viewporter::req::get_viewport(backend, *viewporter, wp_viewport, wl_surface).unwrap();
|
||||
|
||||
let wp_fractional = if let Some(fract_man) = fractional_scale_manager {
|
||||
let fractional = objman.create(wayland::WlDynObj::FractionalScale);
|
||||
let fractional = objman.create(WaylandObject::FractionalScale);
|
||||
wp_fractional_scale_manager_v1::req::get_fractional_scale(
|
||||
fract_man, fractional, wl_surface,
|
||||
backend, *fract_man, fractional, wl_surface,
|
||||
)
|
||||
.unwrap();
|
||||
Some(fractional)
|
||||
@@ -130,21 +148,30 @@ impl Wallpaper {
|
||||
let inner_staging = WallpaperInner::default();
|
||||
|
||||
// Configure the layer surface
|
||||
zwlr_layer_surface_v1::req::set_anchor(layer_surface, 15).unwrap();
|
||||
zwlr_layer_surface_v1::req::set_exclusive_zone(layer_surface, -1).unwrap();
|
||||
zwlr_layer_surface_v1::req::set_margin(layer_surface, 0, 0, 0, 0).unwrap();
|
||||
zwlr_layer_surface_v1::req::set_keyboard_interactivity(
|
||||
zwlr_layer_surface_v1::req::set_anchor(
|
||||
backend,
|
||||
layer_surface,
|
||||
zwlr_layer_surface_v1::keyboard_interactivity::NONE,
|
||||
zwlr_layer_surface_v1::Anchor::TOP
|
||||
| zwlr_layer_surface_v1::Anchor::BOTTOM
|
||||
| zwlr_layer_surface_v1::Anchor::RIGHT
|
||||
| zwlr_layer_surface_v1::Anchor::LEFT,
|
||||
)
|
||||
.unwrap();
|
||||
wl_surface::req::set_buffer_scale(wl_surface, 1).unwrap();
|
||||
zwlr_layer_surface_v1::req::set_exclusive_zone(backend, layer_surface, -1).unwrap();
|
||||
zwlr_layer_surface_v1::req::set_margin(backend, layer_surface, 0, 0, 0, 0).unwrap();
|
||||
zwlr_layer_surface_v1::req::set_keyboard_interactivity(
|
||||
backend,
|
||||
layer_surface,
|
||||
zwlr_layer_surface_v1::KeyboardInteractivity::None,
|
||||
)
|
||||
.unwrap();
|
||||
wl_surface::req::set_buffer_scale(backend, wl_surface, 1).unwrap();
|
||||
|
||||
let frame_callback_handler = FrameCallbackHandler::new(objman, wl_surface);
|
||||
let frame_callback_handler = FrameCallbackHandler::new(backend, objman, wl_surface);
|
||||
// commit so that the compositor send the initial configuration
|
||||
wl_surface::req::commit(wl_surface).unwrap();
|
||||
wl_surface::req::commit(backend, wl_surface).unwrap();
|
||||
|
||||
let pool = BumpPool::new(256, 256, objman, pixel_format);
|
||||
let pool = BumpPool::new(backend, objman, *shm, 256, 256, *pixel_format);
|
||||
|
||||
debug!("New output: {output_name}");
|
||||
Self {
|
||||
@@ -211,7 +238,7 @@ impl Wallpaper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_transform(&mut self, transform: u32) {
|
||||
pub fn set_transform(&mut self, transform: wl_output::Transform) {
|
||||
self.inner_staging.transform = transform;
|
||||
}
|
||||
|
||||
@@ -248,8 +275,13 @@ impl Wallpaper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commit_surface_changes(&mut self, objman: &mut ObjectManager, use_cache: bool) -> bool {
|
||||
use wl_output::transform;
|
||||
pub fn commit_surface_changes(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
use_cache: bool,
|
||||
) -> bool {
|
||||
use wl_output::Transform;
|
||||
let inner = &mut self.inner;
|
||||
let staging = &self.inner_staging;
|
||||
|
||||
@@ -273,7 +305,7 @@ impl Wallpaper {
|
||||
|
||||
let (width, height) = if matches!(
|
||||
staging.transform,
|
||||
transform::_90 | transform::_270 | transform::FLIPPED_90 | transform::FLIPPED_270
|
||||
Transform::_90 | Transform::_270 | Transform::flipped_90 | Transform::flipped_270
|
||||
) {
|
||||
(staging.height, staging.width)
|
||||
} else {
|
||||
@@ -284,13 +316,18 @@ impl Wallpaper {
|
||||
match staging.scale_factor {
|
||||
Scale::Whole(i) => {
|
||||
// unset destination
|
||||
wp_viewport::req::set_destination(self.wp_viewport, -1, -1).unwrap();
|
||||
wl_surface::req::set_buffer_scale(self.wl_surface, i.get()).unwrap();
|
||||
wp_viewport::req::set_destination(backend, self.wp_viewport, -1, -1).unwrap();
|
||||
wl_surface::req::set_buffer_scale(backend, self.wl_surface, i.get()).unwrap();
|
||||
}
|
||||
Scale::Fractional(_) => {
|
||||
wl_surface::req::set_buffer_scale(self.wl_surface, 1).unwrap();
|
||||
wp_viewport::req::set_destination(self.wp_viewport, width.get(), height.get())
|
||||
.unwrap();
|
||||
wl_surface::req::set_buffer_scale(backend, self.wl_surface, 1).unwrap();
|
||||
wp_viewport::req::set_destination(
|
||||
backend,
|
||||
self.wp_viewport,
|
||||
width.get(),
|
||||
height.get(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,6 +345,7 @@ impl Wallpaper {
|
||||
let scale_factor = staging.scale_factor;
|
||||
|
||||
zwlr_layer_surface_v1::req::set_size(
|
||||
backend,
|
||||
self.layer_surface,
|
||||
width.get() as u32,
|
||||
height.get() as u32,
|
||||
@@ -315,11 +353,11 @@ impl Wallpaper {
|
||||
.unwrap();
|
||||
|
||||
let (w, h) = scale_factor.mul_dim(width.get(), height.get());
|
||||
self.pool.resize(w, h);
|
||||
self.pool.resize(backend, w, h);
|
||||
|
||||
self.frame_callback_handler
|
||||
.request_frame_callback(objman, self.wl_surface);
|
||||
wl_surface::req::commit(self.wl_surface).unwrap();
|
||||
.request_frame_callback(backend, objman, self.wl_surface);
|
||||
wl_surface::req::commit(backend, self.wl_surface).unwrap();
|
||||
self.configured
|
||||
.store(true, std::sync::atomic::Ordering::Release);
|
||||
true
|
||||
@@ -350,11 +388,12 @@ impl Wallpaper {
|
||||
|
||||
pub(super) fn try_set_buffer_release_flag(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
buffer: ObjectId,
|
||||
rc_strong_count: usize,
|
||||
) -> bool {
|
||||
self.pool
|
||||
.set_buffer_release_flag(buffer, rc_strong_count != 1)
|
||||
.set_buffer_release_flag(backend, buffer, rc_strong_count != 1)
|
||||
}
|
||||
|
||||
pub fn is_draw_ready(&self) -> bool {
|
||||
@@ -379,14 +418,15 @@ impl Wallpaper {
|
||||
|
||||
pub(super) fn canvas_change<F, T>(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
f: F,
|
||||
) -> T
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> T,
|
||||
{
|
||||
f(self.pool.get_drawable(objman, pixel_format))
|
||||
f(self.pool.get_drawable(backend, objman, pixel_format))
|
||||
}
|
||||
|
||||
pub(super) fn frame_callback_completed(&mut self) {
|
||||
@@ -395,11 +435,12 @@ impl Wallpaper {
|
||||
|
||||
pub(super) fn clear(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
color: [u8; 3],
|
||||
) {
|
||||
self.canvas_change(objman, pixel_format, |canvas| {
|
||||
self.canvas_change(backend, objman, pixel_format, |canvas| {
|
||||
for pixel in canvas.chunks_exact_mut(pixel_format.channels().into()) {
|
||||
pixel[0..3].copy_from_slice(&color);
|
||||
}
|
||||
@@ -410,99 +451,26 @@ impl Wallpaper {
|
||||
debug!("output {:?} - drawing: {}", self.inner.name, img_info);
|
||||
self.img = img_info;
|
||||
}
|
||||
}
|
||||
|
||||
/// attaches all pending buffers and damages all surfaces with one single request
|
||||
pub(crate) fn attach_buffers_and_damage_surfaces(
|
||||
objman: &mut ObjectManager,
|
||||
wallpapers: &[Rc<RefCell<Wallpaper>>],
|
||||
) {
|
||||
#[rustfmt::skip]
|
||||
// Note this is little-endian specific
|
||||
const MSG: [u8; 56] = [
|
||||
0, 0, 0, 0, // wl_surface object id (to be filled)
|
||||
1, 0, // attach opcode
|
||||
20, 0, // msg length
|
||||
0, 0, 0, 0, // attach buffer id (to be filled)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // attach arguments
|
||||
0, 0, 0, 0, // wl_surface object id (to be filled)
|
||||
9, 0, // damage opcode
|
||||
24, 0, // msg length
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // damage first arguments
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // damage second arguments (to be filled)
|
||||
0, 0, 0, 0, // wl_surface object id (to be filled)
|
||||
3, 0, // frame opcode
|
||||
12, 0, // msg length
|
||||
0, 0, 0, 0, // wl_callback object id (to be filled)
|
||||
];
|
||||
let msg: Box<[u8]> = wallpapers
|
||||
.iter()
|
||||
.flat_map(|wallpaper| {
|
||||
let mut wallpaper = wallpaper.borrow_mut();
|
||||
let mut msg = MSG;
|
||||
pub(super) fn destroy(&mut self, backend: &mut Waybackend) {
|
||||
// carefull not to panic here, since we call this on drop
|
||||
|
||||
let buf = wallpaper.pool.get_commitable_buffer();
|
||||
let inner = &wallpaper.inner;
|
||||
let (width, height) = inner
|
||||
.scale_factor
|
||||
.mul_dim(inner.width.get(), inner.height.get());
|
||||
|
||||
// attach
|
||||
msg[0..4].copy_from_slice(&wallpaper.wl_surface.get().to_ne_bytes());
|
||||
msg[8..12].copy_from_slice(&buf.get().to_ne_bytes());
|
||||
|
||||
//damage buffer
|
||||
msg[20..24].copy_from_slice(&wallpaper.wl_surface.get().to_ne_bytes());
|
||||
msg[36..40].copy_from_slice(&width.to_ne_bytes());
|
||||
msg[40..44].copy_from_slice(&height.to_ne_bytes());
|
||||
|
||||
// frame callback
|
||||
let callback = objman.create(WlDynObj::Callback);
|
||||
wallpaper.frame_callback_handler.callback = callback;
|
||||
msg[44..48].copy_from_slice(&wallpaper.wl_surface.get().to_ne_bytes());
|
||||
msg[52..56].copy_from_slice(&callback.get().to_ne_bytes());
|
||||
msg
|
||||
})
|
||||
.collect();
|
||||
unsafe { crate::wayland::wire::send_unchecked(msg.as_ref(), &[]).unwrap() }
|
||||
}
|
||||
|
||||
/// commits multiple wallpapers at once with a single message through the socket
|
||||
pub(crate) fn commit_wallpapers(wallpapers: &[Rc<RefCell<Wallpaper>>]) {
|
||||
// Note this is little-endian specific
|
||||
#[rustfmt::skip]
|
||||
const MSG: [u8; 8] = [
|
||||
0, 0, 0, 0, // wl_surface object id (to be filled)
|
||||
6, 0, // commit opcode
|
||||
8, 0, // msg length
|
||||
];
|
||||
let msg: Box<[u8]> = wallpapers
|
||||
.iter()
|
||||
.flat_map(|wallpaper| {
|
||||
let mut msg = MSG;
|
||||
msg[0..4].copy_from_slice(&wallpaper.borrow().wl_surface.get().to_ne_bytes());
|
||||
msg
|
||||
})
|
||||
.collect();
|
||||
unsafe { crate::wayland::wire::send_unchecked(msg.as_ref(), &[]).unwrap() }
|
||||
}
|
||||
|
||||
impl Drop for Wallpaper {
|
||||
fn drop(&mut self) {
|
||||
// note we shouldn't panic in a drop implementation
|
||||
|
||||
if let Err(e) = wp_viewport::req::destroy(self.wp_viewport) {
|
||||
if let Err(e) = wp_viewport::req::destroy(backend, self.wp_viewport) {
|
||||
error!("error destroying wp_viewport: {e:?}");
|
||||
}
|
||||
|
||||
if let Some(fractional) = self.wp_fractional {
|
||||
if let Err(e) = wp_fractional_scale_v1::req::destroy(fractional) {
|
||||
if let Err(e) = wp_fractional_scale_v1::req::destroy(backend, fractional) {
|
||||
error!("error destroying wp_fractional_scale_v1: {e:?}");
|
||||
}
|
||||
}
|
||||
if let Err(e) = zwlr_layer_surface_v1::req::destroy(self.layer_surface) {
|
||||
|
||||
if let Err(e) = zwlr_layer_surface_v1::req::destroy(backend, self.layer_surface) {
|
||||
error!("error destroying zwlr_layer_surface_v1: {e:?}");
|
||||
}
|
||||
|
||||
self.pool.destroy(backend);
|
||||
|
||||
debug!(
|
||||
"Destroyed output {} - {}",
|
||||
self.inner.name.as_ref().unwrap_or(&"?".to_string()),
|
||||
@@ -511,5 +479,37 @@ impl Drop for Wallpaper {
|
||||
}
|
||||
}
|
||||
|
||||
/// attaches all pending buffers and damages all surfaces with one single request
|
||||
pub(crate) fn attach_buffers_and_damage_surfaces(
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
wallpapers: &[Rc<RefCell<Wallpaper>>],
|
||||
) {
|
||||
for wallpaper in wallpapers {
|
||||
let mut wallpaper = wallpaper.borrow_mut();
|
||||
|
||||
let surface = wallpaper.wl_surface;
|
||||
let buf = wallpaper.pool.get_commitable_buffer();
|
||||
let inner = &wallpaper.inner;
|
||||
let (width, height) = inner
|
||||
.scale_factor
|
||||
.mul_dim(inner.width.get(), inner.height.get());
|
||||
|
||||
wl_surface::req::attach(backend, surface, Some(buf), 0, 0).unwrap();
|
||||
wl_surface::req::damage_buffer(backend, surface, 0, 0, width, height).unwrap();
|
||||
wallpaper
|
||||
.frame_callback_handler
|
||||
.request_frame_callback(backend, objman, surface);
|
||||
}
|
||||
}
|
||||
|
||||
/// commits multiple wallpapers at once with a single message through the socket
|
||||
pub(crate) fn commit_wallpapers(backend: &mut Waybackend, wallpapers: &[Rc<RefCell<Wallpaper>>]) {
|
||||
for wallpaper in wallpapers {
|
||||
let wallpaper = wallpaper.borrow();
|
||||
wl_surface::req::commit(backend, wallpaper.wl_surface).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Wallpaper {}
|
||||
unsafe impl Send for Wallpaper {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use common::{ipc::PixelFormat, mmap::Mmap};
|
||||
use waybackend::{objman::ObjectManager, types::ObjectId, Waybackend};
|
||||
|
||||
use super::{ObjectId, ObjectManager};
|
||||
use crate::WaylandObject;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Buffer {
|
||||
@@ -9,24 +10,25 @@ struct Buffer {
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pool_id: ObjectId,
|
||||
offset: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: u32,
|
||||
format: super::wl_shm::Format,
|
||||
) -> Self {
|
||||
let released = true;
|
||||
let object_id = objman.create(super::WlDynObj::Buffer);
|
||||
super::interfaces::wl_shm_pool::req::create_buffer(
|
||||
pool_id, object_id, offset, width, height, stride, format,
|
||||
let object_id = objman.create(WaylandObject::Buffer);
|
||||
super::wl_shm_pool::req::create_buffer(
|
||||
backend, pool_id, object_id, offset, width, height, stride, format,
|
||||
)
|
||||
.expect("WlShmPool failed to create buffer");
|
||||
Self {
|
||||
object_id,
|
||||
released,
|
||||
released: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +44,8 @@ impl Buffer {
|
||||
self.released = false;
|
||||
}
|
||||
|
||||
fn destroy(self) {
|
||||
if let Err(e) = super::interfaces::wl_buffer::req::destroy(self.object_id) {
|
||||
fn destroy(self, backend: &mut Waybackend) {
|
||||
if let Err(e) = super::wl_buffer::req::destroy(backend, self.object_id) {
|
||||
log::error!("failed to destroy wl_buffer: {e:?}");
|
||||
}
|
||||
}
|
||||
@@ -68,15 +70,17 @@ pub(crate) struct BumpPool {
|
||||
impl BumpPool {
|
||||
/// We assume `width` and `height` have already been multiplied by their scale factor
|
||||
pub(crate) fn new(
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
shm: ObjectId,
|
||||
width: i32,
|
||||
height: i32,
|
||||
objman: &mut ObjectManager,
|
||||
pixel_format: PixelFormat,
|
||||
) -> Self {
|
||||
let len = width as usize * height as usize * pixel_format.channels() as usize;
|
||||
let mmap = Mmap::create(len);
|
||||
let pool_id = objman.create(super::WlDynObj::ShmPool);
|
||||
super::interfaces::wl_shm::req::create_pool(pool_id, &mmap.fd(), len as i32)
|
||||
let pool_id = objman.create(WaylandObject::ShmPool);
|
||||
super::wl_shm::req::create_pool(backend, shm, pool_id, &mmap.fd(), len as i32)
|
||||
.expect("failed to create WlShmPool object");
|
||||
let buffers = Vec::with_capacity(2);
|
||||
|
||||
@@ -96,6 +100,7 @@ impl BumpPool {
|
||||
/// been released
|
||||
pub(crate) fn set_buffer_release_flag(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
buffer_id: ObjectId,
|
||||
is_animating: bool,
|
||||
) -> bool {
|
||||
@@ -103,7 +108,7 @@ impl BumpPool {
|
||||
b.set_released();
|
||||
if !is_animating && self.buffers.iter().all(|b| b.is_released()) {
|
||||
for buffer in self.buffers.drain(..) {
|
||||
buffer.destroy();
|
||||
buffer.destroy(backend);
|
||||
}
|
||||
self.mmap.unmap();
|
||||
}
|
||||
@@ -126,7 +131,12 @@ impl BumpPool {
|
||||
}
|
||||
|
||||
/// resizes the pool and creates a new WlBuffer at the next free offset
|
||||
fn grow(&mut self, objman: &mut ObjectManager, pixel_format: PixelFormat) {
|
||||
fn grow(
|
||||
&mut self,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
) {
|
||||
let len = self.buffer_len(pixel_format);
|
||||
let new_len = self.occupied_bytes(pixel_format) + len;
|
||||
|
||||
@@ -139,18 +149,19 @@ impl BumpPool {
|
||||
panic!("Buffers have grown too big. We cannot allocate any more.")
|
||||
}
|
||||
self.mmap.remap(new_len);
|
||||
super::interfaces::wl_shm_pool::req::resize(self.pool_id, new_len as i32).unwrap();
|
||||
super::wl_shm_pool::req::resize(backend, self.pool_id, new_len as i32).unwrap();
|
||||
}
|
||||
|
||||
let new_buffer_index = self.buffers.len();
|
||||
self.buffers.push(Buffer::new(
|
||||
backend,
|
||||
objman,
|
||||
self.pool_id,
|
||||
self.buffer_offset(new_buffer_index, pixel_format) as i32,
|
||||
self.width,
|
||||
self.height,
|
||||
self.width * pixel_format.channels() as i32,
|
||||
super::globals::wl_shm_format(pixel_format),
|
||||
wl_shm_format(pixel_format),
|
||||
));
|
||||
|
||||
log::info!(
|
||||
@@ -165,22 +176,22 @@ impl BumpPool {
|
||||
/// This function automatically handles copying the previous buffer over onto the new one
|
||||
pub(crate) fn get_drawable(
|
||||
&mut self,
|
||||
objman: &mut ObjectManager,
|
||||
backend: &mut Waybackend,
|
||||
objman: &mut ObjectManager<WaylandObject>,
|
||||
pixel_format: PixelFormat,
|
||||
) -> &mut [u8] {
|
||||
let (i, buf) = match self
|
||||
let i = match self
|
||||
.buffers
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.find(|(_, b)| b.is_released())
|
||||
{
|
||||
Some((i, buf)) => (i, buf),
|
||||
Some((i, _)) => i,
|
||||
None => {
|
||||
self.grow(objman, pixel_format);
|
||||
(self.buffers.len() - 1, self.buffers.last_mut().unwrap())
|
||||
self.grow(backend, objman, pixel_format);
|
||||
self.buffers.len() - 1
|
||||
}
|
||||
};
|
||||
buf.unset_released();
|
||||
|
||||
let len = self.buffer_len(pixel_format);
|
||||
let offset = self.buffer_offset(i, pixel_format);
|
||||
@@ -197,28 +208,39 @@ impl BumpPool {
|
||||
}
|
||||
|
||||
/// gets the last buffer we've drawn to
|
||||
pub(crate) fn get_commitable_buffer(&self) -> ObjectId {
|
||||
self.buffers[self.last_used_buffer].object_id
|
||||
pub(crate) fn get_commitable_buffer(&mut self) -> ObjectId {
|
||||
let buf = &mut self.buffers[self.last_used_buffer];
|
||||
buf.unset_released();
|
||||
buf.object_id
|
||||
}
|
||||
|
||||
/// We assume `width` and `height` have already been multiplied by their scale factor
|
||||
pub(crate) fn resize(&mut self, width: i32, height: i32) {
|
||||
pub(crate) fn resize(&mut self, backend: &mut Waybackend, width: i32, height: i32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
self.last_used_buffer = 0;
|
||||
for buffer in self.buffers.drain(..) {
|
||||
buffer.destroy();
|
||||
buffer.destroy(backend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BumpPool {
|
||||
fn drop(&mut self) {
|
||||
pub(crate) fn destroy(&mut self, backend: &mut Waybackend) {
|
||||
for buffer in self.buffers.drain(..) {
|
||||
buffer.destroy();
|
||||
buffer.destroy(backend);
|
||||
}
|
||||
if let Err(e) = super::interfaces::wl_shm_pool::req::destroy(self.pool_id) {
|
||||
|
||||
if let Err(e) = super::wl_shm_pool::req::destroy(backend, self.pool_id) {
|
||||
log::error!("failed to destroy wl_shm_pool: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn wl_shm_format(pixel_format: PixelFormat) -> super::wl_shm::Format {
|
||||
use super::wl_shm::Format;
|
||||
match pixel_format {
|
||||
PixelFormat::Bgr => Format::bgr888,
|
||||
PixelFormat::Rgb => Format::rgb888,
|
||||
PixelFormat::Xbgr => Format::xbgr8888,
|
||||
PixelFormat::Xrgb => Format::xrgb8888,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,379 +0,0 @@
|
||||
//! `swww-daemon` global variables
|
||||
//!
|
||||
//! There are a lot of `static mut`s in here. The strategy to make them safe is as follows:
|
||||
//!
|
||||
//! First, this module only exposes getter functions to the mutable statics, meaning they cannot be
|
||||
//! mutated anywhere but in here.
|
||||
//!
|
||||
//! Second, the `pub init(..)` function only executes once. We ensure that using an atomic boolean.
|
||||
//! This means we will only be mutating these variables inside that function, once.
|
||||
//!
|
||||
//! In order to be safe, then, all we have to do is make sure we call `init(..)` as early as
|
||||
//! possible in the code, and everything will be fine. If we ever fail to that, we have a failsafe
|
||||
//! with `debug_assert`s in the getter functions, so we would see it explode while debugging.
|
||||
|
||||
use rustix::{
|
||||
fd::{AsFd, BorrowedFd, FromRawFd, OwnedFd},
|
||||
net::SocketAddrAny,
|
||||
};
|
||||
|
||||
use common::ipc::PixelFormat;
|
||||
use log::{debug, error};
|
||||
|
||||
use super::{ObjectId, ObjectManager};
|
||||
use std::{num::NonZeroU32, path::PathBuf, sync::atomic::AtomicBool};
|
||||
|
||||
// all of these objects must always exist for `swww-daemon` to work correctly, so we turn them into
|
||||
// global constants
|
||||
|
||||
pub const WL_DISPLAY: ObjectId = ObjectId(unsafe { NonZeroU32::new_unchecked(1) });
|
||||
pub const WL_REGISTRY: ObjectId = ObjectId(unsafe { NonZeroU32::new_unchecked(2) });
|
||||
pub const WL_COMPOSITOR: ObjectId = ObjectId(unsafe { NonZeroU32::new_unchecked(3) });
|
||||
pub const WL_SHM: ObjectId = ObjectId(unsafe { NonZeroU32::new_unchecked(4) });
|
||||
pub const WP_VIEWPORTER: ObjectId = ObjectId(unsafe { NonZeroU32::new_unchecked(5) });
|
||||
pub const ZWLR_LAYER_SHELL_V1: ObjectId = ObjectId(unsafe { NonZeroU32::new_unchecked(6) });
|
||||
|
||||
/// wl_display and wl_registry will always be available, but these globals could theoretically be
|
||||
/// absent. Nevertheless, they are required for `swww-daemon` to function, so we will need to bind
|
||||
/// all of them.
|
||||
const REQUIRED_GLOBALS: [&str; 4] = [
|
||||
"wl_compositor",
|
||||
"wl_shm",
|
||||
"wp_viewporter",
|
||||
"zwlr_layer_shell_v1",
|
||||
];
|
||||
|
||||
/// Minimal version necessary for `REQUIRED_GLOBALS`
|
||||
const VERSIONS: [u32; 4] = [4, 1, 1, 3];
|
||||
|
||||
/// This is an unsafe static mut that we only ever write to once, during the `init` function call.
|
||||
/// Any other function in this program can only access this variable through the `wayland_fd`
|
||||
/// function, which always creates an immutable reference, which should be safe.
|
||||
static mut WAYLAND_FD: OwnedFd = unsafe { std::mem::zeroed() };
|
||||
|
||||
static INITIALIZED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[must_use]
|
||||
pub fn wayland_fd() -> BorrowedFd<'static> {
|
||||
debug_assert!(INITIALIZED.load(std::sync::atomic::Ordering::Relaxed));
|
||||
let ptr = &raw const WAYLAND_FD;
|
||||
unsafe { &*ptr }.as_fd()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn wl_shm_format(pixel_format: PixelFormat) -> u32 {
|
||||
match pixel_format {
|
||||
PixelFormat::Xrgb => super::interfaces::wl_shm::format::XRGB8888,
|
||||
PixelFormat::Xbgr => super::interfaces::wl_shm::format::XBGR8888,
|
||||
PixelFormat::Rgb => super::interfaces::wl_shm::format::RGB888,
|
||||
PixelFormat::Bgr => super::interfaces::wl_shm::format::BGR888,
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that this function assumes the logger has already been set up
|
||||
pub fn init(pixel_format: Option<PixelFormat>) -> InitState {
|
||||
if INITIALIZED.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
panic!("trying to run initialization code twice");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
WAYLAND_FD = connect();
|
||||
}
|
||||
let mut initializer = Initializer::new(pixel_format);
|
||||
|
||||
// the only globals that can break catastrophically are WAYLAND_FD and OBJECT_MANAGER, that we
|
||||
// have just initialized above. So this is safe
|
||||
INITIALIZED.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
// these functions already require for the wayland file descriptor and the object manager to
|
||||
// have been initialized, which we just did above
|
||||
super::interfaces::wl_display::req::get_registry().unwrap();
|
||||
super::interfaces::wl_display::req::sync(ObjectId::new(NonZeroU32::new(3).unwrap())).unwrap();
|
||||
|
||||
const IDS: [ObjectId; 4] = [WL_COMPOSITOR, WL_SHM, WP_VIEWPORTER, ZWLR_LAYER_SHELL_V1];
|
||||
|
||||
// this loop will process and store all advertised wayland globals, storing their global name
|
||||
// in the Initializer struct
|
||||
while !initializer.should_exit {
|
||||
let (msg, payload) = super::wire::WireMsg::recv().unwrap();
|
||||
if msg.sender_id().get() == 3 {
|
||||
super::interfaces::wl_callback::event(&mut initializer, msg, payload);
|
||||
} else if msg.sender_id() == WL_DISPLAY {
|
||||
super::interfaces::wl_display::event(&mut initializer, msg, payload);
|
||||
} else if msg.sender_id() == WL_REGISTRY {
|
||||
super::interfaces::wl_registry::event(&mut initializer, msg, payload);
|
||||
} else {
|
||||
panic!("Did not receive expected global events from registry")
|
||||
}
|
||||
}
|
||||
|
||||
// if we failed to find some necessary global, panic
|
||||
if let Some((_, missing)) = initializer
|
||||
.global_names
|
||||
.iter()
|
||||
.zip(REQUIRED_GLOBALS)
|
||||
.find(|(name, _)| **name == 0)
|
||||
{
|
||||
panic!("Compositor does not implement required interface: {missing}");
|
||||
}
|
||||
|
||||
// bind all the globals we need
|
||||
for (i, name) in initializer.global_names.into_iter().enumerate() {
|
||||
let id = IDS[i];
|
||||
let interface = REQUIRED_GLOBALS[i];
|
||||
let version = VERSIONS[i];
|
||||
super::interfaces::wl_registry::req::bind(name, id, interface, version).unwrap();
|
||||
}
|
||||
|
||||
// bind fractional scale, if it is supported
|
||||
if let Some(fractional_scale_manager) = initializer.fractional_scale.as_ref() {
|
||||
super::interfaces::wl_registry::req::bind(
|
||||
fractional_scale_manager.name.get(),
|
||||
fractional_scale_manager.id,
|
||||
"wp_fractional_scale_manager_v1",
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let callback_id = initializer.callback_id();
|
||||
super::interfaces::wl_display::req::sync(callback_id).unwrap();
|
||||
initializer.should_exit = false;
|
||||
// this loop will go through all the advertised wl_shm format, selecting one for the
|
||||
// PIXEL_FORMAT global, if `--format <..>` wasn't passed as a command line argument
|
||||
while !initializer.should_exit {
|
||||
let (msg, payload) = super::wire::WireMsg::recv().unwrap();
|
||||
match msg.sender_id() {
|
||||
// in case there are errors
|
||||
WL_DISPLAY => super::interfaces::wl_display::event(&mut initializer, msg, payload),
|
||||
WL_REGISTRY => super::interfaces::wl_registry::event(&mut initializer, msg, payload),
|
||||
WL_SHM => super::interfaces::wl_shm::event(&mut initializer, msg, payload),
|
||||
other => {
|
||||
if other == callback_id {
|
||||
super::interfaces::wl_callback::event(&mut initializer, msg, payload);
|
||||
} else {
|
||||
error!("received unexpected event from compositor during initialization")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializer.into_init_state()
|
||||
}
|
||||
|
||||
/// mostly copy-pasted from `wayland-client.rs`
|
||||
fn connect() -> OwnedFd {
|
||||
if let Ok(txt) = std::env::var("WAYLAND_SOCKET") {
|
||||
// We should connect to the provided WAYLAND_SOCKET
|
||||
let fd = txt
|
||||
.parse::<i32>()
|
||||
.expect("invalid fd in WAYLAND_SOCKET env var");
|
||||
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
|
||||
|
||||
let socket_addr =
|
||||
rustix::net::getsockname(&fd).expect("failed to get wayland socket address");
|
||||
if let SocketAddrAny::Unix(_) = socket_addr {
|
||||
fd
|
||||
} else {
|
||||
panic!("socket address {:?} is not a unix socket", socket_addr);
|
||||
}
|
||||
} else {
|
||||
let socket_name: PathBuf = std::env::var_os("WAYLAND_DISPLAY")
|
||||
.unwrap_or_else(|| {
|
||||
log::warn!("WAYLAND_DISPLAY is not set! Defaulting to wayland-0");
|
||||
std::ffi::OsString::from("wayland-0")
|
||||
})
|
||||
.into();
|
||||
|
||||
let socket_path = if socket_name.is_absolute() {
|
||||
socket_name
|
||||
} else {
|
||||
let mut socket_path: PathBuf = std::env::var_os("XDG_RUNTIME_DIR")
|
||||
.unwrap_or_else(|| {
|
||||
log::warn!("XDG_RUNTIME_DIR is not set! Defaulting to /run/user/UID");
|
||||
let uid = rustix::process::getuid();
|
||||
std::ffi::OsString::from(format!("/run/user/{}", uid.as_raw()))
|
||||
})
|
||||
.into();
|
||||
|
||||
socket_path.push(socket_name);
|
||||
socket_path
|
||||
};
|
||||
|
||||
match std::os::unix::net::UnixStream::connect(&socket_path) {
|
||||
Ok(stream) => stream.into(),
|
||||
Err(e) => panic!("failed to connect to wayland socket at {socket_path:?}: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FractionalScaleManager {
|
||||
id: ObjectId,
|
||||
name: NonZeroU32,
|
||||
}
|
||||
|
||||
impl FractionalScaleManager {
|
||||
pub fn id(&self) -> ObjectId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to do all the initialization in this file
|
||||
struct Initializer {
|
||||
objman: ObjectManager,
|
||||
pixel_format: PixelFormat,
|
||||
global_names: [u32; REQUIRED_GLOBALS.len()],
|
||||
output_names: Vec<u32>,
|
||||
fractional_scale: Option<FractionalScaleManager>,
|
||||
forced_shm_format: bool,
|
||||
should_exit: bool,
|
||||
}
|
||||
|
||||
/// Helper struct to expose all of the initialized state
|
||||
pub struct InitState {
|
||||
pub output_names: Vec<u32>,
|
||||
pub fractional_scale: Option<FractionalScaleManager>,
|
||||
pub objman: ObjectManager,
|
||||
pub pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
impl Initializer {
|
||||
fn new(cli_format: Option<PixelFormat>) -> Self {
|
||||
Self {
|
||||
objman: ObjectManager::new(),
|
||||
global_names: [0; REQUIRED_GLOBALS.len()],
|
||||
output_names: Vec::new(),
|
||||
fractional_scale: None,
|
||||
forced_shm_format: cli_format.is_some(),
|
||||
should_exit: false,
|
||||
pixel_format: cli_format.unwrap_or(PixelFormat::Xrgb),
|
||||
}
|
||||
}
|
||||
|
||||
fn callback_id(&self) -> ObjectId {
|
||||
if self.fractional_scale.is_some() {
|
||||
ObjectId(unsafe { NonZeroU32::new_unchecked(8) })
|
||||
} else {
|
||||
ObjectId(unsafe { NonZeroU32::new_unchecked(7) })
|
||||
}
|
||||
}
|
||||
|
||||
fn into_init_state(self) -> InitState {
|
||||
debug!("Initialization Over");
|
||||
InitState {
|
||||
output_names: self.output_names,
|
||||
fractional_scale: self.fractional_scale,
|
||||
objman: self.objman,
|
||||
pixel_format: self.pixel_format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_names(&self) -> &[u32] {
|
||||
&self.output_names
|
||||
}
|
||||
|
||||
pub fn fractional_scale(&self) -> Option<&FractionalScaleManager> {
|
||||
self.fractional_scale.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::interfaces::wl_display::HasObjman for Initializer {
|
||||
fn objman(&mut self) -> &mut ObjectManager {
|
||||
&mut self.objman
|
||||
}
|
||||
}
|
||||
|
||||
impl super::interfaces::wl_display::EvHandler for Initializer {
|
||||
fn delete_id(&mut self, id: u32) {
|
||||
if id == 3 // initial callback for the roundtrip
|
||||
|| self.fractional_scale.is_none() && id == 7
|
||||
|| self.fractional_scale.is_some() && id == 8
|
||||
{
|
||||
self.should_exit = true;
|
||||
} else {
|
||||
panic!("ObjectId removed during initialization! This should be very rare, which is why we don't deal with it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::interfaces::wl_callback::EvHandler for Initializer {
|
||||
fn done(&mut self, sender_id: ObjectId, _callback_data: u32) {
|
||||
debug!(
|
||||
"Initialization: {} callback done",
|
||||
if sender_id.get() == 3 {
|
||||
"first"
|
||||
} else {
|
||||
"second"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl super::interfaces::wl_registry::EvHandler for Initializer {
|
||||
fn global(&mut self, name: u32, interface: &str, version: u32) {
|
||||
match interface {
|
||||
"wp_fractional_scale_manager_v1" => {
|
||||
self.fractional_scale = Some(FractionalScaleManager {
|
||||
id: ObjectId(unsafe { NonZeroU32::new_unchecked(7) }),
|
||||
name: name.try_into().unwrap(),
|
||||
});
|
||||
self.objman.set_fractional_scale_support(true);
|
||||
}
|
||||
"wl_output" => {
|
||||
if version < 4 {
|
||||
error!("wl_output implementation must have at least version 4 for swww-daemon")
|
||||
} else {
|
||||
self.output_names.push(name);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for (i, global) in REQUIRED_GLOBALS.iter().enumerate() {
|
||||
if *global == interface {
|
||||
if version < VERSIONS[i] {
|
||||
panic!(
|
||||
"{interface} version must be at least {} for swww",
|
||||
VERSIONS[i]
|
||||
);
|
||||
}
|
||||
self.global_names[i] = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn global_remove(&mut self, _name: u32) {
|
||||
panic!("Global removed during initialization! This should be very rare, which is why we don't deal with it");
|
||||
}
|
||||
}
|
||||
|
||||
impl super::interfaces::wl_shm::EvHandler for Initializer {
|
||||
fn format(&mut self, format: u32) {
|
||||
match format {
|
||||
super::interfaces::wl_shm::format::XRGB8888 => {
|
||||
debug!("available shm format: Xrbg");
|
||||
}
|
||||
super::interfaces::wl_shm::format::XBGR8888 => {
|
||||
debug!("available shm format: Xbgr");
|
||||
if !self.forced_shm_format && self.pixel_format == PixelFormat::Xrgb {
|
||||
self.pixel_format = PixelFormat::Xbgr;
|
||||
}
|
||||
}
|
||||
super::interfaces::wl_shm::format::RGB888 => {
|
||||
debug!("available shm format: Rbg");
|
||||
if !self.forced_shm_format && self.pixel_format != PixelFormat::Bgr {
|
||||
self.pixel_format = PixelFormat::Rgb
|
||||
}
|
||||
}
|
||||
super::interfaces::wl_shm::format::BGR888 => {
|
||||
debug!("available shm format: Bgr");
|
||||
if !self.forced_shm_format {
|
||||
self.pixel_format = PixelFormat::Bgr
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,179 +1,3 @@
|
||||
//! Our own custom wayland implementation
|
||||
//!
|
||||
//! The primary reason for doing this is that `wayland-client.rs` offers a very flexible api, at the
|
||||
//! cost of ergonomics: there are lots of Arcs everywhere, lots of trait implementations with
|
||||
//! nothing in them, to the point the developers even have macros just to create dummy trait
|
||||
//! implementations, since it's so annoying.
|
||||
//!
|
||||
//! Our own implementation can make several improvements:
|
||||
//! * we make all the globals that always in our program exist `const`s, so that they can be
|
||||
//! accessed anywhere within the code
|
||||
//! * we make the wayland file descriptor a global variable, so it can be accessed anywhere
|
||||
//! within the code
|
||||
//! * we don't buffer the wayland socket connection, instead just sending the message all at once
|
||||
//! every time. This, combined with the two points above, mean we can make request from multiple
|
||||
//! threads without having to keep passing weak references to a Backend struct (like how it
|
||||
//! happens with `wayland-client.rs`).
|
||||
//! * we have a much simpler (from what I can tell), object id manager implementation. That we've
|
||||
//! also made global, so it can be called anywhere.
|
||||
//!
|
||||
//! Furthermore, this also prevents any changes to `wayland-client.rs` from affecting us. We are
|
||||
//! now completely independent from them.
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
pub mod bump_pool;
|
||||
pub mod globals;
|
||||
pub mod interfaces;
|
||||
pub mod wire;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ObjectId(NonZeroU32);
|
||||
|
||||
impl ObjectId {
|
||||
#[must_use]
|
||||
pub const fn get(&self) -> u32 {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn new(value: NonZeroU32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn null() -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum WlDynObj {
|
||||
Output,
|
||||
Surface,
|
||||
Region,
|
||||
LayerSurface,
|
||||
Buffer,
|
||||
ShmPool,
|
||||
Callback,
|
||||
Viewport,
|
||||
FractionalScale,
|
||||
}
|
||||
|
||||
/// Object Manager for creating, removing, and maintaining Wayland Objects
|
||||
pub struct ObjectManager {
|
||||
/// stores the object types. The position in this vector + the base offset is the object id
|
||||
/// for example, if objects[0] == LayerSurface, then the object of id 0 + BASE_OFFSET = 7 is of
|
||||
/// the type "LayerSurface"
|
||||
objects: Vec<Option<WlDynObj>>,
|
||||
/// the next id we ought to generate
|
||||
next: u32,
|
||||
fractional_scale_support: bool,
|
||||
}
|
||||
|
||||
impl ObjectManager {
|
||||
/// Ids 1-6, inclusive, are all already taken by the globals in `globals.rs`
|
||||
const BASE_OFFSET: u32 = 7;
|
||||
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
objects: Vec::new(),
|
||||
next: 0,
|
||||
fractional_scale_support: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// get the type of the wayland object from its id
|
||||
///
|
||||
/// Returns
|
||||
/// * 'Some(WlDynObj)' if the object still exists
|
||||
/// * 'None' if the object was already deleted
|
||||
#[must_use]
|
||||
pub fn get(&self, object_id: ObjectId) -> Option<WlDynObj> {
|
||||
let offset = Self::BASE_OFFSET + self.fractional_scale_support as u32;
|
||||
let pos = object_id.get() - offset;
|
||||
self.objects[pos as usize]
|
||||
}
|
||||
|
||||
/// creates a new Id to use in requests
|
||||
#[must_use]
|
||||
pub fn create(&mut self, object: WlDynObj) -> ObjectId {
|
||||
let offset = Self::BASE_OFFSET + self.fractional_scale_support as u32;
|
||||
if self.next as usize == self.objects.len() {
|
||||
self.next += 1;
|
||||
self.objects.push(Some(object));
|
||||
ObjectId::new(unsafe { NonZeroU32::new(self.next + offset - 1).unwrap_unchecked() })
|
||||
} else {
|
||||
let i = self.next as usize;
|
||||
self.objects[i] = Some(object);
|
||||
|
||||
// update next to the next available id
|
||||
self.next += 1;
|
||||
while (self.next as usize) < self.objects.len() {
|
||||
if self.objects[self.next as usize].is_none() {
|
||||
break;
|
||||
}
|
||||
self.next += 1;
|
||||
}
|
||||
|
||||
ObjectId::new(unsafe { NonZeroU32::new(i as u32 + offset).unwrap_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
/// removes the wayland object.
|
||||
///
|
||||
/// Removing the same element twice currently works just fine and does not panic,
|
||||
/// but that may change in the future
|
||||
pub fn remove(&mut self, object_id: ObjectId) {
|
||||
let offset = Self::BASE_OFFSET + self.fractional_scale_support as u32;
|
||||
let pos = object_id.get() - offset;
|
||||
self.objects[pos as usize] = None;
|
||||
if pos < self.next {
|
||||
self.next = pos;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fractional_scale_support(&mut self, fractional_scale_support: bool) {
|
||||
self.fractional_scale_support = fractional_scale_support;
|
||||
}
|
||||
|
||||
pub fn fractional_scale_support(&self) -> bool {
|
||||
self.fractional_scale_support
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
fn obj_from_u32(u: u32) -> ObjectId {
|
||||
ObjectId::new(NonZeroU32::new(u).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creating_object_ids() {
|
||||
let mut manager = ObjectManager::new();
|
||||
let id1 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id1, obj_from_u32(ObjectManager::BASE_OFFSET));
|
||||
let id2 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id2, obj_from_u32(ObjectManager::BASE_OFFSET + 1));
|
||||
let id3 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id3, obj_from_u32(ObjectManager::BASE_OFFSET + 2));
|
||||
|
||||
manager.remove(id2);
|
||||
let id4 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id4, id2);
|
||||
|
||||
manager.remove(id1);
|
||||
let id5 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id5, id1);
|
||||
|
||||
manager.remove(id2);
|
||||
manager.remove(id1);
|
||||
let id6 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id6, id1);
|
||||
|
||||
let id7 = manager.create(WlDynObj::Region);
|
||||
assert_eq!(id7, id2);
|
||||
}
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/wayland_protocols.rs"));
|
||||
|
||||
@@ -1,477 +0,0 @@
|
||||
//! Implementation of the Wayland Wire Protocol
|
||||
//!
|
||||
//! There are some things that are specific for `swww-daemon` (for example, our ancillary buffer
|
||||
//! for receiving socket messages is always empty, since none of the events we care about have file
|
||||
//! descriptors), but I tried to actually make it fairly complete. This means types like `WlFixed`
|
||||
//! exist even if they aren't used at all in the rest of the codebase.
|
||||
|
||||
use rustix::{
|
||||
fd::{AsRawFd, BorrowedFd, OwnedFd},
|
||||
io, net,
|
||||
};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use super::{globals::wayland_fd, ObjectId};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WaylandPayload(Box<[u32]>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WireMsg {
|
||||
sender_id: ObjectId,
|
||||
op: u16,
|
||||
fds: Box<[OwnedFd]>,
|
||||
cur: u16, // the message is at most 1 << 15 bytes long
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WlSlice<'a>(&'a [u8]);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WlStr<'a>(&'a str);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct WlFixed(i32);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NewId<'a> {
|
||||
id: ObjectId,
|
||||
interface: &'a str,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
impl WaylandPayload {
|
||||
#[must_use]
|
||||
pub const fn get(&self) -> &[u32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl WireMsg {
|
||||
pub fn recv() -> rustix::io::Result<(Self, WaylandPayload)> {
|
||||
let fds = Vec::new();
|
||||
|
||||
let mut header_buf = [0u32; 2];
|
||||
|
||||
// we don't need these because no events we care about send file descriptors
|
||||
let mut ancillary_buf = [0; 0];
|
||||
let mut control = net::RecvAncillaryBuffer::new(i32_slice_to_u8_mut(&mut ancillary_buf));
|
||||
|
||||
let iov = io::IoSliceMut::new(u32_slice_to_u8_mut(&mut header_buf));
|
||||
net::recvmsg(
|
||||
wayland_fd(),
|
||||
&mut [iov],
|
||||
&mut control,
|
||||
net::RecvFlags::empty(),
|
||||
)?;
|
||||
|
||||
let sender_id = ObjectId(
|
||||
NonZeroU32::new(header_buf[0])
|
||||
.expect("received a message from compositor with a null sender id"),
|
||||
);
|
||||
let size = (header_buf[1] >> 16) as usize - 8;
|
||||
let op = (header_buf[1] & 0xFFFF) as u16;
|
||||
|
||||
let mut payload = vec![0u32; size >> 2];
|
||||
|
||||
if size > 0 {
|
||||
// this should not fail with INTR, because otherwise our socket's internal buffer will
|
||||
// be left in an inconsistent state (a message without a header)
|
||||
rustix::io::retry_on_intr(|| {
|
||||
let iov = io::IoSliceMut::new(u32_slice_to_u8_mut(&mut payload));
|
||||
net::recvmsg(
|
||||
wayland_fd(),
|
||||
&mut [iov],
|
||||
&mut control,
|
||||
net::RecvFlags::WAITALL,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
sender_id,
|
||||
op,
|
||||
fds: fds.into_boxed_slice(),
|
||||
cur: 0,
|
||||
},
|
||||
WaylandPayload(payload.into_boxed_slice()),
|
||||
))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn into_fds(self) -> Box<[OwnedFd]> {
|
||||
self.fds
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn sender_id(&self) -> ObjectId {
|
||||
ObjectId(self.sender_id.0)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn op(&self) -> u16 {
|
||||
self.op
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_i32(&mut self, payload: &WaylandPayload) -> i32 {
|
||||
self.cur += 1;
|
||||
payload.get()[self.cur as usize - 1] as i32
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_u32(&mut self, payload: &WaylandPayload) -> u32 {
|
||||
self.cur += 1;
|
||||
payload.get()[self.cur as usize - 1]
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_fixed(&mut self, payload: &WaylandPayload) -> WlFixed {
|
||||
self.cur += 1;
|
||||
WlFixed::from(payload.get()[self.cur as usize - 1])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_string<'a>(&mut self, payload: &'a WaylandPayload) -> &'a str {
|
||||
let len = payload.get()[self.cur as usize] as usize;
|
||||
|
||||
unsafe {
|
||||
// remember to skip the length
|
||||
let ptr = payload.get().as_ptr().add(self.cur as usize + 1);
|
||||
|
||||
// the len sent by the protocol includes the '0', but we do not need it
|
||||
let cast = std::slice::from_raw_parts(ptr.cast(), len - 1);
|
||||
self.cur += 1 + ((len + 3) >> 2) as u16;
|
||||
std::str::from_utf8(cast).expect("string received is not valid utf8")
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_object(&mut self, payload: &WaylandPayload) -> Option<ObjectId> {
|
||||
self.cur += 1;
|
||||
NonZeroU32::new(payload.get()[self.cur as usize - 1]).map(ObjectId)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_new_specified_id(&mut self, payload: &WaylandPayload) -> ObjectId {
|
||||
self.next_object(payload).unwrap()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_new_unspecified_id<'a>(&mut self, payload: &'a WaylandPayload) -> NewId<'a> {
|
||||
let interface = self.next_string(payload);
|
||||
let version = self.next_u32(payload);
|
||||
let id = self.next_new_specified_id(payload);
|
||||
|
||||
NewId {
|
||||
id,
|
||||
interface,
|
||||
version,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_array<'a>(&mut self, payload: &'a WaylandPayload) -> &'a [u8] {
|
||||
let len = payload.get()[self.cur as usize] as usize;
|
||||
|
||||
unsafe {
|
||||
let ptr = payload.get().as_ptr().add(self.cur as usize + 1); // skip the length
|
||||
self.cur += 1 + ((len + 3) >> 2) as u16;
|
||||
std::slice::from_raw_parts(ptr.cast(), len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WireMsgBuilder {
|
||||
msg: Vec<u32>,
|
||||
fds: Vec<i32>,
|
||||
}
|
||||
|
||||
impl WireMsgBuilder {
|
||||
#[must_use]
|
||||
pub fn new(sender_id: ObjectId, op: u16) -> Self {
|
||||
let msg = vec![sender_id.get(), op as u32];
|
||||
Self {
|
||||
msg,
|
||||
fds: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_i32(&mut self, i: i32) {
|
||||
self.msg.push(i as u32);
|
||||
}
|
||||
|
||||
pub fn add_u32(&mut self, u: u32) {
|
||||
self.msg.push(u);
|
||||
}
|
||||
|
||||
pub fn add_fixed(&mut self, fixed: WlFixed) {
|
||||
self.msg.push(fixed.0 as u32)
|
||||
}
|
||||
|
||||
pub fn add_string(&mut self, s: &str) {
|
||||
WlStr(s).encode(&mut self.msg);
|
||||
}
|
||||
|
||||
pub fn add_object(&mut self, object_id: Option<ObjectId>) {
|
||||
match object_id {
|
||||
Some(id) => self.msg.push(id.get()),
|
||||
None => self.msg.push(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_new_specified_id(&mut self, object_id: ObjectId) {
|
||||
self.msg.push(object_id.get());
|
||||
}
|
||||
|
||||
pub fn add_new_unspecified_id(&mut self, object_id: ObjectId, interface: &str, version: u32) {
|
||||
self.add_string(interface);
|
||||
self.add_u32(version);
|
||||
self.add_new_specified_id(object_id);
|
||||
}
|
||||
|
||||
pub fn add_array(&mut self, array: &[u8]) {
|
||||
WlSlice(array).encode(&mut self.msg);
|
||||
}
|
||||
|
||||
pub fn add_fd<'a, 'b: 'a>(&'a mut self, fd: &'b impl AsRawFd) {
|
||||
self.fds.push(fd.as_raw_fd());
|
||||
}
|
||||
|
||||
pub fn send(self) -> rustix::io::Result<()> {
|
||||
let Self { mut msg, fds } = self;
|
||||
let len = msg.len() << 2;
|
||||
// put the correct length in the upper part of the header's second word
|
||||
msg[1] |= (len as u32) << 16;
|
||||
|
||||
let mut borrowed_fds = Vec::with_capacity(fds.len());
|
||||
for fd in fds {
|
||||
borrowed_fds.push(unsafe { BorrowedFd::borrow_raw(fd) });
|
||||
}
|
||||
unsafe { send_unchecked(u32_slice_to_u8(&msg), &borrowed_fds) }
|
||||
}
|
||||
}
|
||||
|
||||
/// try to send a raw message through the wayland socket. We do no input validation whatsoever
|
||||
pub unsafe fn send_unchecked(msg: &[u8], fds: &[BorrowedFd]) -> rustix::io::Result<()> {
|
||||
let iov = io::IoSlice::new(msg);
|
||||
let mut control_buf = [0u8; rustix::cmsg_space!(ScmRights(1))];
|
||||
let mut control = net::SendAncillaryBuffer::new(&mut control_buf);
|
||||
let msg = net::SendAncillaryMessage::ScmRights(fds);
|
||||
control.push(msg);
|
||||
net::sendmsg(wayland_fd(), &[iov], &mut control, net::SendFlags::NOSIGNAL).map(|_| ())
|
||||
}
|
||||
|
||||
impl WlSlice<'_> {
|
||||
#[must_use]
|
||||
pub const fn get(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn encode(&self, buf: &mut Vec<u32>) {
|
||||
let len = self.0.len().next_multiple_of(4);
|
||||
buf.push(len as u32);
|
||||
buf.reserve(len >> 2);
|
||||
unsafe {
|
||||
// dst is the next free position in the buffer
|
||||
let dst = buf.as_ptr().add(buf.len()) as *mut u8;
|
||||
|
||||
// copy all the bytes
|
||||
// SAFETY: we've ensured the buf's pointer has the necessary size above
|
||||
std::ptr::copy_nonoverlapping(self.0.as_ptr(), dst, self.0.len());
|
||||
|
||||
// 0 initialize the padding
|
||||
for i in self.0.len()..len {
|
||||
dst.add(i).write(0);
|
||||
}
|
||||
|
||||
// set the len to the values we've just written
|
||||
buf.set_len(buf.len() + (len >> 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b [u8]> for WlSlice<'a>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
#[must_use]
|
||||
fn from(bytes: &'b [u8]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl WlStr<'_> {
|
||||
#[must_use]
|
||||
pub const fn get(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn encode(&self, buf: &mut Vec<u32>) {
|
||||
let bytes = self.0.as_bytes();
|
||||
// add one for the null terminator
|
||||
let len = (bytes.len() + 1).next_multiple_of(4);
|
||||
buf.push(len as u32);
|
||||
buf.reserve(len >> 2);
|
||||
unsafe {
|
||||
// dst is the next free position in the buffer
|
||||
let dst = buf.as_ptr().add(buf.len()) as *mut u8;
|
||||
|
||||
// copy all the bytes
|
||||
// SAFETY: we've ensured the buf's pointer has the necessary size above
|
||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), dst, bytes.len());
|
||||
|
||||
// 0 initialize the padding and the null terminator
|
||||
for i in bytes.len()..len {
|
||||
dst.add(i).write(0);
|
||||
}
|
||||
|
||||
// set the len to the values we've just written
|
||||
buf.set_len(buf.len() + (len >> 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b str> for WlStr<'a>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
#[must_use]
|
||||
fn from(s: &'b str) -> Self {
|
||||
Self(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for WlFixed {
|
||||
#[must_use]
|
||||
fn from(value: i32) -> Self {
|
||||
Self(value * 256)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for WlFixed {
|
||||
#[must_use]
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value as i32 * 256)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&WlFixed> for i32 {
|
||||
#[must_use]
|
||||
fn from(val: &WlFixed) -> Self {
|
||||
val.0 / 256
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for WlFixed {
|
||||
#[must_use]
|
||||
fn from(value: f64) -> Self {
|
||||
let d = value + (3i64 << (51 - 8)) as f64;
|
||||
Self(d.to_bits() as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&WlFixed> for f64 {
|
||||
#[must_use]
|
||||
fn from(val: &WlFixed) -> Self {
|
||||
let i = ((1023i64 + 44i64) << 52) + (1i64 << 51) + val.0 as i64;
|
||||
let d = f64::from_bits(i as u64);
|
||||
d - (3i64 << 43) as f64
|
||||
}
|
||||
}
|
||||
|
||||
impl NewId<'_> {
|
||||
#[must_use]
|
||||
pub const fn id(&self) -> &ObjectId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn interface(&self) -> &str {
|
||||
self.interface
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn version(&self) -> u32 {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
const fn u32_slice_to_u8(src: &[u32]) -> &[u8] {
|
||||
let len = src.len() << 2;
|
||||
unsafe { std::slice::from_raw_parts(src.as_ptr() as *mut u8, len) }
|
||||
}
|
||||
|
||||
fn u32_slice_to_u8_mut(src: &mut [u32]) -> &mut [u8] {
|
||||
let len = src.len() << 2;
|
||||
unsafe { std::slice::from_raw_parts_mut(src.as_mut_ptr() as *mut u8, len) }
|
||||
}
|
||||
|
||||
fn i32_slice_to_u8_mut(src: &mut [i32]) -> &mut [u8] {
|
||||
let len = src.len() << 2;
|
||||
unsafe { std::slice::from_raw_parts_mut(src.as_mut_ptr() as *mut u8, len) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fixed_creation() {
|
||||
assert_eq!(WlFixed::from(-1), WlFixed::from(0xFFFFFFFFu32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_encoding() {
|
||||
let mut buf = Vec::new();
|
||||
|
||||
let arr = [1u8, 2, 3, 4, 5, 6, 7];
|
||||
WlSlice::from(arr.as_ref()).encode(&mut buf);
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
let expected = vec![8, 0x04030201u32, 0x00070605];
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
let expected = vec![8, 0x01020304u32, 0x05060700];
|
||||
|
||||
assert_eq!(buf, expected);
|
||||
buf.clear();
|
||||
|
||||
let arr = [1u8, 2, 3, 4];
|
||||
WlSlice::from(arr.as_ref()).encode(&mut buf);
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
let expected = vec![4, 0x04030201u32];
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
let expected = vec![4, 0x01020304u32];
|
||||
|
||||
assert_eq!(buf, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str_encoding() {
|
||||
let mut buf = Vec::new();
|
||||
|
||||
WlStr::from("hello world").encode(&mut buf);
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
let expected = vec![12, 0x6C6C6568u32, 0x6F77206F, 0x00646C72];
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
let expected = vec![12, 0x06865C6Cu32, 0x6F20776F, 0x726C6400];
|
||||
|
||||
assert_eq!(buf, expected);
|
||||
buf.clear();
|
||||
|
||||
WlStr::from("hell").encode(&mut buf);
|
||||
#[cfg(target_endian = "little")]
|
||||
let expected = vec![8, 0x6C6C6568u32, 0];
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
let expected = vec![8, 0x06865C6Cu32, 0];
|
||||
|
||||
assert_eq!(buf, expected);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ allow = [
|
||||
"BSD-3-Clause",
|
||||
"GPL-3.0",
|
||||
"MIT",
|
||||
"Unicode-DFS-2016",
|
||||
"Unicode-3.0"
|
||||
]
|
||||
|
||||
[sources]
|
||||
|
||||
390
protocols/wlr-layer-shell-unstable-v1.xml
Normal file
390
protocols/wlr-layer-shell-unstable-v1.xml
Normal file
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_layer_shell_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2017 Drew DeVault
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwlr_layer_shell_v1" version="4">
|
||||
<description summary="create surfaces that are layers of the desktop">
|
||||
Clients can use this interface to assign the surface_layer role to
|
||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||
rendered with a defined z-depth respective to each other. They may also be
|
||||
anchored to the edges and corners of a screen and specify input handling
|
||||
semantics. This interface should be suitable for the implementation of
|
||||
many desktop shell components, and a broad number of other applications
|
||||
that interact with the desktop.
|
||||
</description>
|
||||
|
||||
<request name="get_layer_surface">
|
||||
<description summary="create a layer_surface from a surface">
|
||||
Create a layer surface for an existing surface. This assigns the role of
|
||||
layer_surface, or raises a protocol error if another role is already
|
||||
assigned.
|
||||
|
||||
Creating a layer surface from a wl_surface which has a buffer attached
|
||||
or committed is a client error, and any attempts by a client to attach
|
||||
or manipulate a buffer prior to the first layer_surface.configure call
|
||||
must also be treated as errors.
|
||||
|
||||
After creating a layer_surface object and setting it up, the client
|
||||
must perform an initial commit without any buffer attached.
|
||||
The compositor will reply with a layer_surface.configure event.
|
||||
The client must acknowledge it and is then allowed to attach a buffer
|
||||
to map the surface.
|
||||
|
||||
You may pass NULL for output to allow the compositor to decide which
|
||||
output to use. Generally this will be the one that the user most
|
||||
recently interacted with.
|
||||
|
||||
Clients can specify a namespace that defines the purpose of the layer
|
||||
surface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||
</enum>
|
||||
|
||||
<enum name="layer">
|
||||
<description summary="available layers for surfaces">
|
||||
These values indicate which layers a surface can be rendered in. They
|
||||
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||
will typically be rendered between the bottom and top layers.
|
||||
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||
Multiple surfaces can share a single layer, and ordering within a
|
||||
single layer is undefined.
|
||||
</description>
|
||||
|
||||
<entry name="background" value="0"/>
|
||||
<entry name="bottom" value="1"/>
|
||||
<entry name="top" value="2"/>
|
||||
<entry name="overlay" value="3"/>
|
||||
</enum>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="destroy" type="destructor" since="3">
|
||||
<description summary="destroy the layer_shell object">
|
||||
This request indicates that the client will not use the layer_shell
|
||||
object any more. Objects that have been created through this instance
|
||||
are not affected.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_layer_surface_v1" version="4">
|
||||
<description summary="layer metadata interface">
|
||||
An interface that may be implemented by a wl_surface, for surfaces that
|
||||
are designed to be rendered as a layer of a stacked desktop-like
|
||||
environment.
|
||||
|
||||
Layer surface state (layer, size, anchor, exclusive zone,
|
||||
margin, interactivity) is double-buffered, and will be applied at the
|
||||
time wl_surface.commit of the corresponding wl_surface is called.
|
||||
|
||||
Attaching a null buffer to a layer surface unmaps it.
|
||||
|
||||
Unmapping a layer_surface means that the surface cannot be shown by the
|
||||
compositor until it is explicitly mapped again. The layer_surface
|
||||
returns to the state it had right after layer_shell.get_layer_surface.
|
||||
The client can re-map the surface by performing a commit without any
|
||||
buffer attached, waiting for a configure event and handling it as usual.
|
||||
</description>
|
||||
|
||||
<request name="set_size">
|
||||
<description summary="sets the size of the surface">
|
||||
Sets the size of the surface in surface-local coordinates. The
|
||||
compositor will display the surface centered with respect to its
|
||||
anchors.
|
||||
|
||||
If you pass 0 for either value, the compositor will assign it and
|
||||
inform you of the assignment in the configure event. You must set your
|
||||
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||
protocol error. Both values are 0 by default.
|
||||
|
||||
Size is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="set_anchor">
|
||||
<description summary="configures the anchor point of the surface">
|
||||
Requests that the compositor anchor the surface to the specified edges
|
||||
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||
'left'), then the anchor point will be the intersection of the edges
|
||||
(e.g. the top left corner of the output); otherwise the anchor point
|
||||
will be centered on that edge, or in the center if none is specified.
|
||||
|
||||
Anchor is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="anchor" type="uint" enum="anchor"/>
|
||||
</request>
|
||||
|
||||
<request name="set_exclusive_zone">
|
||||
<description summary="configures the exclusive geometry of this surface">
|
||||
Requests that the compositor avoids occluding an area with other
|
||||
surfaces. The compositor's use of this information is
|
||||
implementation-dependent - do not assume that this region will not
|
||||
actually be occluded.
|
||||
|
||||
A positive value is only meaningful if the surface is anchored to one
|
||||
edge or an edge and both perpendicular edges. If the surface is not
|
||||
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||
to only two parallel edges or anchored to all edges, a positive value
|
||||
will be treated the same as zero.
|
||||
|
||||
A positive zone is the distance from the edge in surface-local
|
||||
coordinates to consider exclusive.
|
||||
|
||||
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||
how they should interact with surfaces that do. If set to zero, the
|
||||
surface indicates that it would like to be moved to avoid occluding
|
||||
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||
indicates that it would not like to be moved to accommodate for other
|
||||
surfaces, and the compositor should extend it all the way to the edges
|
||||
it is anchored to.
|
||||
|
||||
For example, a panel might set its exclusive zone to 10, so that
|
||||
maximized shell surfaces are not shown on top of it. A notification
|
||||
might set its exclusive zone to 0, so that it is moved to avoid
|
||||
occluding the panel, but shell surfaces are shown underneath it. A
|
||||
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||
they stretch below or over the panel.
|
||||
|
||||
The default value is 0.
|
||||
|
||||
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="zone" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_margin">
|
||||
<description summary="sets a margin from the anchor point">
|
||||
Requests that the surface be placed some distance away from the anchor
|
||||
point on the output, in surface-local coordinates. Setting this value
|
||||
for edges you are not anchored to has no effect.
|
||||
|
||||
The exclusive zone includes the margin.
|
||||
|
||||
Margin is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="top" type="int"/>
|
||||
<arg name="right" type="int"/>
|
||||
<arg name="bottom" type="int"/>
|
||||
<arg name="left" type="int"/>
|
||||
</request>
|
||||
|
||||
<enum name="keyboard_interactivity">
|
||||
<description summary="types of keyboard interaction possible for a layer shell surface">
|
||||
Types of keyboard interaction possible for layer shell surfaces. The
|
||||
rationale for this is twofold: (1) some applications are not interested
|
||||
in keyboard events and not allowing them to be focused can improve the
|
||||
desktop experience; (2) some applications will want to take exclusive
|
||||
keyboard focus.
|
||||
</description>
|
||||
|
||||
<entry name="none" value="0">
|
||||
<description summary="no keyboard focus is possible">
|
||||
This value indicates that this surface is not interested in keyboard
|
||||
events and the compositor should never assign it the keyboard focus.
|
||||
|
||||
This is the default value, set for newly created layer shell surfaces.
|
||||
|
||||
This is useful for e.g. desktop widgets that display information or
|
||||
only have interaction with non-keyboard input devices.
|
||||
</description>
|
||||
</entry>
|
||||
<entry name="exclusive" value="1">
|
||||
<description summary="request exclusive keyboard focus">
|
||||
Request exclusive keyboard focus if this surface is above the shell surface layer.
|
||||
|
||||
For the top and overlay layers, the seat will always give
|
||||
exclusive keyboard focus to the top-most layer which has keyboard
|
||||
interactivity set to exclusive. If this layer contains multiple
|
||||
surfaces with keyboard interactivity set to exclusive, the compositor
|
||||
determines the one receiving keyboard events in an implementation-
|
||||
defined manner. In this case, no guarantee is made when this surface
|
||||
will receive keyboard focus (if ever).
|
||||
|
||||
For the bottom and background layers, the compositor is allowed to use
|
||||
normal focus semantics.
|
||||
|
||||
This setting is mainly intended for applications that need to ensure
|
||||
they receive all keyboard events, such as a lock screen or a password
|
||||
prompt.
|
||||
</description>
|
||||
</entry>
|
||||
<entry name="on_demand" value="2" since="4">
|
||||
<description summary="request regular keyboard focus semantics">
|
||||
This requests the compositor to allow this surface to be focused and
|
||||
unfocused by the user in an implementation-defined manner. The user
|
||||
should be able to unfocus this surface even regardless of the layer
|
||||
it is on.
|
||||
|
||||
Typically, the compositor will want to use its normal mechanism to
|
||||
manage keyboard focus between layer shell surfaces with this setting
|
||||
and regular toplevels on the desktop layer (e.g. click to focus).
|
||||
Nevertheless, it is possible for a compositor to require a special
|
||||
interaction to focus or unfocus layer shell surfaces (e.g. requiring
|
||||
a click even if focus follows the mouse normally, or providing a
|
||||
keybinding to switch focus between layers).
|
||||
|
||||
This setting is mainly intended for desktop shell components (e.g.
|
||||
panels) that allow keyboard interaction. Using this option can allow
|
||||
implementing a desktop shell that can be fully usable without the
|
||||
mouse.
|
||||
</description>
|
||||
</entry>
|
||||
</enum>
|
||||
|
||||
<request name="set_keyboard_interactivity">
|
||||
<description summary="requests keyboard events">
|
||||
Set how keyboard events are delivered to this surface. By default,
|
||||
layer shell surfaces do not receive keyboard events; this request can
|
||||
be used to change this.
|
||||
|
||||
This setting is inherited by child surfaces set by the get_popup
|
||||
request.
|
||||
|
||||
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||
you do not want to receive them, set the input region on your surface
|
||||
to an empty region.
|
||||
|
||||
Keyboard interactivity is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
|
||||
</request>
|
||||
|
||||
<request name="get_popup">
|
||||
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||
should have been created via xdg_surface::get_popup with the parent set
|
||||
to NULL, and this request must be invoked before committing the popup's
|
||||
initial state.
|
||||
|
||||
See the documentation of xdg_popup for more details about what an
|
||||
xdg_popup is and how it is used.
|
||||
</description>
|
||||
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||
</request>
|
||||
|
||||
<request name="ack_configure">
|
||||
<description summary="ack a configure event">
|
||||
When a configure event is received, if a client commits the
|
||||
surface in response to the configure event, then the client
|
||||
must make an ack_configure request sometime before the commit
|
||||
request, passing along the serial of the configure event.
|
||||
|
||||
If the client receives multiple configure events before it
|
||||
can respond to one, it only has to ack the last configure event.
|
||||
|
||||
A client is not required to commit immediately after sending
|
||||
an ack_configure request - it may even ack_configure several times
|
||||
before its next surface commit.
|
||||
|
||||
A client may send multiple ack_configure requests before committing, but
|
||||
only the last request sent before a commit indicates which configure
|
||||
event the client really is responding to.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the layer_surface">
|
||||
This request destroys the layer surface.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="configure">
|
||||
<description summary="suggest a surface change">
|
||||
The configure event asks the client to resize its surface.
|
||||
|
||||
Clients should arrange their surface for the new states, and then send
|
||||
an ack_configure request with the serial sent in this configure event at
|
||||
some point before committing the new surface.
|
||||
|
||||
The client is free to dismiss all but the last configure event it
|
||||
received.
|
||||
|
||||
The width and height arguments specify the size of the window in
|
||||
surface-local coordinates.
|
||||
|
||||
The size is a hint, in the sense that the client is free to ignore it if
|
||||
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||
surface will be centered on this axis.
|
||||
|
||||
If the width or height arguments are zero, it means the client should
|
||||
decide its own window dimension.
|
||||
</description>
|
||||
<arg name="serial" type="uint"/>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</event>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="surface should be closed">
|
||||
The closed event is sent by the compositor when the surface will no
|
||||
longer be shown. The output may have been destroyed or the user may
|
||||
have asked for it to be removed. Further changes to the surface will be
|
||||
ignored. The client should destroy the resource after receiving this
|
||||
event, and create a new surface if they so choose.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
|
||||
</enum>
|
||||
|
||||
<enum name="anchor" bitfield="true">
|
||||
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||
</enum>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<request name="set_layer" since="2">
|
||||
<description summary="change the layer of the surface">
|
||||
Change the layer that the surface is rendered on.
|
||||
|
||||
Layer is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
24
tests/Cargo.toml
Normal file
24
tests/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "tests"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license-file.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0"
|
||||
image = { version = "0.25", default-features = false, features = [] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[[test]]
|
||||
name = "integration"
|
||||
path = "integration.rs"
|
||||
|
||||
[[test]]
|
||||
name = "spell-check"
|
||||
path = "spell_check.rs"
|
||||
@@ -16,7 +16,7 @@ fn spell_check_code_and_man_pages() {
|
||||
"doc/generated", // skip the generated documentation
|
||||
"src", // client
|
||||
"daemon/src", // daemon
|
||||
"common/src", // common code
|
||||
"common/src", // common code
|
||||
"doc", // man pages
|
||||
"example_scripts", // scripts
|
||||
"CHANGELOG.md",
|
||||
|
||||
Reference in New Issue
Block a user