From b5eb33401be5264591021355c5661ef728e2f811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Fri, 4 Apr 2025 17:44:45 -0300 Subject: [PATCH 1/5] rewrite using waybackend In the process, we also fixed: * integration tests not running * some clippy warnings * memory leaks with extra wayland buffers being created --- Cargo.lock | 79 +- Cargo.toml | 2 +- client/Cargo.toml | 7 +- common/Cargo.toml | 4 +- common/src/compression/comp/mod.rs | 6 +- common/src/compression/comp/sse2.rs | 8 +- common/src/ipc/socket.rs | 4 +- common/src/ipc/transmit.rs | 5 +- common/src/ipc/types.rs | 4 +- common/src/mmap.rs | 11 +- daemon/Cargo.toml | 13 +- daemon/build.rs | 33 + daemon/src/animations/mod.rs | 32 +- daemon/src/animations/transitions.rs | 57 +- daemon/src/main.rs | 530 +++-- daemon/src/wallpaper.rs | 308 +-- daemon/src/wayland/bump_pool.rs | 86 +- daemon/src/wayland/globals.rs | 379 ---- daemon/src/wayland/interfaces.rs | 2272 --------------------- daemon/src/wayland/mod.rs | 178 +- daemon/src/wayland/wire.rs | 477 ----- protocols/wlr-layer-shell-unstable-v1.xml | 390 ++++ tests/Cargo.toml | 24 + tests/spell_check.rs | 2 +- 24 files changed, 1157 insertions(+), 3754 deletions(-) create mode 100644 daemon/build.rs delete mode 100644 daemon/src/wayland/globals.rs delete mode 100644 daemon/src/wayland/interfaces.rs delete mode 100644 daemon/src/wayland/wire.rs create mode 100644 protocols/wlr-layer-shell-unstable-v1.xml create mode 100644 tests/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index c04a399d..c88725e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", @@ -538,12 +538,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.3" @@ -570,9 +564,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" 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", ] @@ -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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fab4aa6b3b3fb33504a4e78848894f5878fead2de2e11380b26d8b923cd0ba8" +dependencies = [ + "bitflags 2.9.0", + "log", + "rustix", +] + +[[package]] +name = "waybackend-scanner" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f641aeacf2d43eb3bae0f9b05b51719d27d973e11bd9a6a8f28d81798bdadf" +dependencies = [ + "pkg-config", + "proc-macro2", + "quick-xml", + "quote", + "syn", +] + [[package]] name = "weezl" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index e89b3d07..a3c24a5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/client/Cargo.toml b/client/Cargo.toml index 94e4b064..6f0ff4e9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -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" diff --git a/common/Cargo.toml b/common/Cargo.toml index 81571551..97c7d402 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -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]] diff --git a/common/src/compression/comp/mod.rs b/common/src/compression/comp/mod.rs index ce5e97e4..b02ab04a 100644 --- a/common/src/compression/comp/mod.rs +++ b/common/src/compression/comp/mod.rs @@ -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}" + ); } } } diff --git a/common/src/compression/comp/sse2.rs b/common/src/compression/comp/sse2.rs index 24333d97..5a703e93 100644 --- a/common/src/compression/comp/sse2.rs +++ b/common/src/compression/comp/sse2.rs @@ -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}" + ); } } diff --git a/common/src/ipc/socket.rs b/common/src/ipc/socket.rs index 48e14f38..f5765313 100644 --- a/common/src/ipc/socket.rs +++ b/common/src/ipc/socket.rs @@ -100,7 +100,7 @@ impl IpcSocket { // 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 { 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)) } diff --git a/common/src/ipc/transmit.rs b/common/src/ipc/transmit.rs index b6ec48dd..575a6843 100644 --- a/common/src/ipc/transmit.rs +++ b/common/src/ipc/transmit.rs @@ -1,3 +1,4 @@ +use std::mem::MaybeUninit; use std::thread; use std::time::Duration; @@ -232,7 +233,7 @@ impl IpcSocket { 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 IpcSocket { pub fn recv(&self) -> Result { 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); diff --git a/common/src/ipc/types.rs b/common/src/ipc/types.rs index 6644c877..ba28c0a7 100644 --- a/common/src/ipc/types.rs +++ b/common/src/ipc/types.rs @@ -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; diff --git a/common/src/mmap.rs b/common/src/mmap.rs index ced90e1d..863d8bfb 100644 --- a/common/src/mmap.rs +++ b/common/src/mmap.rs @@ -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 { 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 { diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 929b1b70..94cd9bb4 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -10,17 +10,18 @@ license-file.workspace = true workspace = true [dependencies] +common = { workspace = true } + +waybackend = { version = "0.2" } 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.2", features = ["build-script"] } diff --git a/daemon/build.rs b/daemon/build.rs new file mode 100644 index 00000000..de242b6c --- /dev/null +++ b/daemon/build.rs @@ -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, + ); +} diff --git a/daemon/src/animations/mod.rs b/daemon/src/animations/mod.rs index d853183a..57191ae8 100644 --- a/daemon/src/animations/mod.rs +++ b/daemon/src/animations/mod.rs @@ -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, + 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, + 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}"); diff --git a/daemon/src/animations/transitions.rs b/daemon/src/animations/transitions.rs index e47498d3..fccf2c57 100644 --- a/daemon/src/animations/transitions.rs +++ b/daemon/src/animations/transitions.rs @@ -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, Instant) { let bezier = BezierCurve::from( @@ -44,14 +45,17 @@ impl None { fn run( &mut self, - objman: &mut ObjectManager, + backend: &mut Waybackend, + objman: &mut ObjectManager, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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, pixel_format: PixelFormat, wallpapers: &mut [Rc>], 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)) diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 94d70dcf..0d1250b3 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -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, + registry: ObjectId, + compositor: ObjectId, + shm: ObjectId, + viewporter: ObjectId, + layer_shell: ObjectId, pixel_format: PixelFormat, wallpapers: Vec>>, transition_animators: Vec, image_animators: Vec, use_cache: bool, fractional_scale_manager: Option, - poll_time: PollTime, -} -impl Daemon { - fn new(init_state: InitState, no_cache: bool) -> Self { - let InitState { - output_names, - fractional_scale, - objman, - pixel_format, - } = init_state; + /// 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, - assert_eq!( - fractional_scale.is_some(), - objman.fractional_scale_support() - ); + forced_shm_format: bool, - log::info!("Selected wl_shm format: {pixel_format:?}"); + // This are the globals we have received before we figured out which shm format to use + output_globals: Option>, + // This callback is from the sync request we make when calling `Daemon::new` + callback: Option, +} - let mut daemon = Self { +impl Daemon { + fn new( + mut backend: waybackend::Waybackend, + mut objman: objman::ObjectManager, + shm_format: Option, + no_cache: bool, + output_globals: Vec, + ) -> 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, + 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::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); + } } -} -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)); - } + 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> { // 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::::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}; - - 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:?}")), - } + use rustix::event::{PollFd, PollFlags}; + use wayland::*; + use WaylandObject::*; + + 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 for i32 { - fn from(value: PollTime) -> Self { - value as i32 - } -} - struct Logger { level_filter: LevelFilter, start: std::time::Instant, diff --git a/daemon/src/wallpaper.rs b/daemon/src/wallpaper.rs index 75f2c973..d865b181 100644 --- a/daemon/src/wallpaper.rs +++ b/daemon/src/wallpaper.rs @@ -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, + 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, + 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, - desc: Option, +pub struct WallpaperInner { + pub name: Option, + pub desc: Option, 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, - layer_surface: ObjectId, + pub wl_surface: ObjectId, + pub wp_viewport: ObjectId, + pub wp_fractional: Option, + 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, - 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(); - - let wl_surface = objman.create(wayland::WlDynObj::Surface); - wl_compositor::req::create_surface(wl_surface).unwrap(); - - let region = objman.create(wayland::WlDynObj::Region); - wl_compositor::req::create_region(region).unwrap(); - - wl_surface::req::set_input_region(wl_surface, Some(region)).unwrap(); - wl_region::req::destroy(region).unwrap(); - - let layer_surface = objman.create(wayland::WlDynObj::LayerSurface); + 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(WaylandObject::Surface); + wl_compositor::req::create_surface(backend, *compositor, wl_surface).unwrap(); + + let region = objman.create(WaylandObject::Region); + wl_compositor::req::create_region(backend, *compositor, 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(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_anchor( + backend, + layer_surface, + 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(); + 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::keyboard_interactivity::NONE, + zwlr_layer_surface_v1::KeyboardInteractivity::None, ) .unwrap(); - wl_surface::req::set_buffer_scale(wl_surface, 1).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, + 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( &mut self, - objman: &mut ObjectManager, + backend: &mut Waybackend, + objman: &mut ObjectManager, 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, 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>], -) { - #[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; - - 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>]) { - // 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 + pub(super) fn destroy(&mut self, backend: &mut Waybackend) { + // carefull not to panic here, since we call this on drop - 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, + wallpapers: &[Rc>], +) { + 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>]) { + 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 {} diff --git a/daemon/src/wayland/bump_pool.rs b/daemon/src/wayland/bump_pool.rs index 9c52f41f..42dc1c1e 100644 --- a/daemon/src/wayland/bump_pool.rs +++ b/daemon/src/wayland/bump_pool.rs @@ -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, 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, + 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, + 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, 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, + } +} diff --git a/daemon/src/wayland/globals.rs b/daemon/src/wayland/globals.rs deleted file mode 100644 index 2b1d4320..00000000 --- a/daemon/src/wayland/globals.rs +++ /dev/null @@ -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) -> 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::() - .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, - fractional_scale: Option, - forced_shm_format: bool, - should_exit: bool, -} - -/// Helper struct to expose all of the initialized state -pub struct InitState { - pub output_names: Vec, - pub fractional_scale: Option, - pub objman: ObjectManager, - pub pixel_format: PixelFormat, -} - -impl Initializer { - fn new(cli_format: Option) -> 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 - } - } - _ => (), - } - } -} diff --git a/daemon/src/wayland/interfaces.rs b/daemon/src/wayland/interfaces.rs deleted file mode 100644 index 209634a9..00000000 --- a/daemon/src/wayland/interfaces.rs +++ /dev/null @@ -1,2272 +0,0 @@ -//! Wayland Interfaces we care about -//! -//! I only bothered implementing the interfaces we actually use. The initial implementation was -//! done with an ad-hoc wayland scanner, but then I later refined it to make it more specific to -//! what `swww-daemon` needs. Specifically, we use a lot of the globals in `super::globals` to -//! simplify the code. - -use super::{ - globals, - wire::{WaylandPayload, WireMsg, WireMsgBuilder, WlFixed}, - ObjectId, ObjectManager, -}; - -///core global object -/// -///The core global object. This is a special singleton object. It -///is used for internal Wayland protocol features. -pub mod wl_display { - use super::*; - - /// This is a special interface to make it possible to have a generic implementation for this - /// interface - pub trait HasObjman { - fn objman(&mut self) -> &mut ObjectManager; - } - - pub trait EvHandler: HasObjman { - ///fatal error event - /// - ///The error event is sent out when a fatal (non-recoverable) - ///error has occurred. The object_id argument is the object - ///where the error occurred, most often in response to a request - ///to that object. The code identifies the error and is defined - ///by the object interface. As such, each interface defines its - ///own set of error codes. The message is a brief description - ///of the error, for (debugging) convenience. - fn error(&mut self, object_id: ObjectId, code: u32, message: &str) { - let interface = match object_id { - globals::WL_DISPLAY => "wl_display", - globals::WL_REGISTRY => "wl_registry", - globals::WL_COMPOSITOR => "wl_compositor", - globals::WL_SHM => "wl_shm", - globals::WP_VIEWPORTER => "wp_viewporter", - globals::ZWLR_LAYER_SHELL_V1 => "zwlr_layer_shell_v1", - other => match self.objman().get(other) { - Some(super::super::WlDynObj::Output) => "wl_output", - Some(super::super::WlDynObj::Surface) => "wl_surface", - Some(super::super::WlDynObj::Region) => "wl_region", - Some(super::super::WlDynObj::LayerSurface) => "zwlr_layer_surface_v1", - Some(super::super::WlDynObj::Buffer) => "wl_buffer", - Some(super::super::WlDynObj::ShmPool) => "wl_shm_pool", - Some(super::super::WlDynObj::Callback) => "wl_callback", - Some(super::super::WlDynObj::Viewport) => "wl_viewport", - Some(super::super::WlDynObj::FractionalScale) => "wp_fractional_scale_v1", - None => "???", - }, - }; - - panic!("Protocol error on interface {interface}. Code {code}: {message}"); - } - ///acknowledge object ID deletion - /// - ///This event is used internally by the object ID management - ///logic. When a client deletes an object that it had created, - ///the server will send this event to acknowledge that it has - ///seen the delete request. When the client receives this event, - ///it will know that it can safely reuse the object ID. - fn delete_id(&mut self, id: u32); - } - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let object_id = wire_msg.next_object(&payload).unwrap(); - let code = wire_msg.next_u32(&payload); - let message = wire_msg.next_string(&payload); - state.error(object_id, code, message); - } - 1 => state.delete_id(wire_msg.next_u32(&payload)), - e => log::error!("unrecognized event opcode: {e} for interface wl_display"), - } - } - - ///Requests for this interface - pub mod req { - use super::*; - ///asynchronous roundtrip - /// - ///The sync request asks the server to emit the 'done' event - ///on the returned wl_callback object. Since requests are - ///handled in-order and events are delivered in-order, this can - ///be used as a barrier to ensure all previous requests and the - ///resulting events have been handled. - /// - ///The object returned by this request will be destroyed by the - ///compositor after the callback is fired and as such the client must not - ///attempt to use it after that point. - /// - ///The callback_data passed in the callback is the event serial. - pub fn sync(callback: ObjectId) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WL_DISPLAY, 0); - wire_msg_builder.add_new_specified_id(callback); - wire_msg_builder.send() - } - ///get global registry object - /// - ///This request creates a registry object that allows the client - ///to list and bind the global objects available from the - ///compositor. - /// - ///It should be noted that the server side resources consumed in - ///response to a get_registry request can only be released when the - ///client disconnects, not when the client side proxy is destroyed. - ///Therefore, clients should invoke get_registry as infrequently as - ///possible to avoid wasting memory. - pub fn get_registry() -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WL_DISPLAY, 1); - wire_msg_builder.add_new_specified_id(globals::WL_REGISTRY); - wire_msg_builder.send() - } - } - ///global error values - /// - ///These errors are global and can be emitted in response to any - ///server request. - pub mod error { - ///server couldn't find object - pub const INVALID_OBJECT: u32 = 0u32; - ///method doesn't exist on the specified interface or malformed request - pub const INVALID_METHOD: u32 = 1u32; - ///server is out of memory - pub const NO_MEMORY: u32 = 2u32; - ///implementation error in compositor - pub const IMPLEMENTATION: u32 = 3u32; - } -} -///global registry object -/// -///The singleton global registry object. The server has a number of -///global objects that are available to all clients. These objects -///typically represent an actual object in the server (for example, -///an input device) or they are singleton objects that provide -///extension functionality. -/// -///When a client creates a registry object, the registry object -///will emit a global event for each global currently in the -///registry. Globals come and go as a result of device or -///monitor hotplugs, reconfiguration or other events, and the -///registry will send out global and global_remove events to -///keep the client up to date with the changes. To mark the end -///of the initial burst of events, the client can use the -///wl_display.sync request immediately after calling -///wl_display.get_registry. -/// -///A client can bind to a global object by using the bind -///request. This creates a client-side handle that lets the object -///emit events to the client and lets the client invoke requests on -///the object. -pub mod wl_registry { - use super::*; - - pub trait EvHandler { - ///announce global object - /// - ///Notify the client of global objects. - /// - ///The event notifies the client that a global object with - ///the given name is now available, and it implements the - ///given version of the given interface. - fn global(&mut self, name: u32, interface: &str, version: u32); - ///announce removal of global object - /// - ///Notify the client of removed global objects. - /// - ///This event notifies the client that the global identified - ///by name is no longer available. If the client bound to - ///the global using the bind request, the client should now - ///destroy that object. - /// - ///The object remains valid and requests to the object will be - ///ignored until the client destroys it, to avoid races between - ///the global going away and a client sending a request to it. - fn global_remove(&mut self, name: u32); - } - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let name = wire_msg.next_u32(&payload); - let interface = wire_msg.next_string(&payload); - let version = wire_msg.next_u32(&payload); - state.global(name, interface, version); - } - 1 => state.global_remove(wire_msg.next_u32(&payload)), - e => log::error!("unrecognized event opcode: {e} for interface wl_registry"), - } - } - - ///Requests for this interface - pub mod req { - use super::*; - ///bind an object to the display - /// - ///Binds a new, client-created object to the server using the - ///specified name as the identifier. - pub fn bind( - name: u32, - id: ObjectId, - id_interface: &str, - id_version: u32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WL_REGISTRY, 0); - wire_msg_builder.add_u32(name); - wire_msg_builder.add_new_unspecified_id(id, id_interface, id_version); - wire_msg_builder.send() - } - } -} -///callback object -/// -///Clients can handle the 'done' event to get notified when -///the related request is done. -/// -///Note, because wl_callback objects are created from multiple independent -///factory interfaces, the wl_callback interface is frozen at version 1. -pub mod wl_callback { - use super::*; - - pub trait EvHandler { - ///done event - /// - ///Notify the client when the related request is done. - /// - ///THIS IS A DESTRUCTOR - fn done(&mut self, sender_id: ObjectId, callback_data: u32); - } - - ///Requests for this interface - pub mod req {} - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let callback_data = wire_msg.next_u32(&payload); - state.done(wire_msg.sender_id(), callback_data); - } - e => log::error!("unrecognized event opcode: {e} for interface wl_callback"), - } - } -} -///the compositor singleton -/// -///A compositor. This object is a singleton global. The -///compositor is in charge of combining the contents of multiple -///surfaces into one displayable output. -pub mod wl_compositor { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///create new surface - /// - ///Ask the compositor to create a new surface. - pub fn create_surface(id: ObjectId) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WL_COMPOSITOR, 0); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.send() - } - ///create new region - /// - ///Ask the compositor to create a new region. - pub fn create_region(id: ObjectId) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WL_COMPOSITOR, 1); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.send() - } - } -} -///a shared memory pool -/// -///The wl_shm_pool object encapsulates a piece of memory shared -///between the compositor and client. Through the wl_shm_pool -///object, the client can allocate shared memory wl_buffer objects. -///All objects created through the same pool share the same -///underlying mapped memory. Reusing the mapped memory avoids the -///setup/teardown overhead and is useful when interactively resizing -///a surface or for many small buffers. -pub mod wl_shm_pool { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///create a buffer from the pool - /// - ///Create a wl_buffer object from the pool. - /// - ///The buffer is created offset bytes into the pool and has - ///width and height as specified. The stride argument specifies - ///the number of bytes from the beginning of one row to the beginning - ///of the next. The format is the pixel format of the buffer and - ///must be one of those advertised through the wl_shm.format event. - /// - ///A buffer will keep a reference to the pool it was created from - ///so it is valid to destroy the pool immediately after creating - ///a buffer from it. - #[allow(clippy::too_many_arguments)] - pub fn create_buffer( - sender_id: ObjectId, - id: ObjectId, - offset: i32, - width: i32, - height: i32, - stride: i32, - format: u32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.add_i32(offset); - wire_msg_builder.add_i32(width); - wire_msg_builder.add_i32(height); - wire_msg_builder.add_i32(stride); - wire_msg_builder.add_u32(format); - wire_msg_builder.send() - } - - ///destroy the pool - /// - ///Destroy the shared memory pool. - /// - ///The mmapped memory will be released when all - ///buffers that have been created from this pool - ///are gone. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 1); - wire_msg_builder.send() - } - ///change the size of the pool mapping - /// - ///This request will cause the server to remap the backing memory - ///for the pool from the file descriptor passed when the pool was - ///created, but using the new size. This request can only be - ///used to make the pool bigger. - /// - ///This request only changes the amount of bytes that are mmapped - ///by the server and does not touch the file corresponding to the - ///file descriptor passed at creation time. It is the client's - ///responsibility to ensure that the file is at least as big as - ///the new pool size. - pub fn resize(sender_id: ObjectId, size: i32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 2); - wire_msg_builder.add_i32(size); - wire_msg_builder.send() - } - } -} -///shared memory support -/// -///A singleton global object that provides support for shared -///memory. -/// -///Clients can create wl_shm_pool objects using the create_pool -///request. -/// -///On binding the wl_shm object one or more format events -///are emitted to inform clients about the valid pixel formats -///that can be used for buffers. -pub mod wl_shm { - use super::*; - - pub trait EvHandler { - ///pixel format description - /// - ///Informs the client about a valid pixel format that - ///can be used for buffers. Known formats include - ///argb8888 and xrgb8888. - fn format(&mut self, format: u32); - } - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => state.format(wire_msg.next_u32(&payload)), - e => log::error!("unrecognized event opcode: {e} for interface wl_shm"), - } - } - - ///Requests for this interface - pub mod req { - use super::*; - ///create a shm pool - /// - ///Create a new wl_shm_pool object. - /// - ///The pool can be used to create shared memory based buffer - ///objects. The server will mmap size bytes of the passed file - ///descriptor, to use as backing memory for the pool. - pub fn create_pool( - id: ObjectId, - fd: &impl rustix::fd::AsRawFd, - size: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WL_SHM, 0); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.add_fd(fd); - wire_msg_builder.add_i32(size); - wire_msg_builder.send() - } - } - pub mod error { - ///buffer format is not known - pub const INVALID_FORMAT: u32 = 0u32; - ///invalid size or stride during pool or buffer creation - pub const INVALID_STRIDE: u32 = 1u32; - ///mmapping the file descriptor failed - pub const INVALID_FD: u32 = 2u32; - } - ///pixel formats - /// - ///This describes the memory layout of an individual pixel. - /// - ///All renderers should support argb8888 and xrgb8888 but any other - ///formats are optional and may not be supported by the particular - ///renderer in use. - /// - ///The drm format codes match the macros defined in drm_fourcc.h, except - ///argb8888 and xrgb8888. The formats actually supported by the compositor - ///will be reported by the format event. - /// - ///For all wl_shm formats and unless specified in another protocol - ///extension, pre-multiplied alpha is used for pixel values. - pub mod format { - ///32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian - pub const ARGB8888: u32 = 0u32; - ///32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian - pub const XRGB8888: u32 = 1u32; - ///8-bit color index format, [7:0] C - pub const C8: u32 = 538982467u32; - ///8-bit RGB format, [7:0] R:G:B 3:3:2 - pub const RGB332: u32 = 943867730u32; - ///8-bit BGR format, [7:0] B:G:R 2:3:3 - pub const BGR233: u32 = 944916290u32; - ///16-bit xRGB format, [15:0] x:R:G:B 4:4:4:4 little endian - pub const XRGB4444: u32 = 842093144u32; - ///16-bit xBGR format, [15:0] x:B:G:R 4:4:4:4 little endian - pub const XBGR4444: u32 = 842089048u32; - ///16-bit RGBx format, [15:0] R:G:B:x 4:4:4:4 little endian - pub const RGBX4444: u32 = 842094674u32; - ///16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian - pub const BGRX4444: u32 = 842094658u32; - ///16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian - pub const ARGB4444: u32 = 842093121u32; - ///16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian - pub const ABGR4444: u32 = 842089025u32; - ///16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian - pub const RGBA4444: u32 = 842088786u32; - ///16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian - pub const BGRA4444: u32 = 842088770u32; - ///16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian - pub const XRGB1555: u32 = 892424792u32; - ///16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian - pub const XBGR1555: u32 = 892420696u32; - ///16-bit RGBx 5551 format, [15:0] R:G:B:x 5:5:5:1 little endian - pub const RGBX5551: u32 = 892426322u32; - ///16-bit BGRx 5551 format, [15:0] B:G:R:x 5:5:5:1 little endian - pub const BGRX5551: u32 = 892426306u32; - ///16-bit ARGB 1555 format, [15:0] A:R:G:B 1:5:5:5 little endian - pub const ARGB1555: u32 = 892424769u32; - ///16-bit ABGR 1555 format, [15:0] A:B:G:R 1:5:5:5 little endian - pub const ABGR1555: u32 = 892420673u32; - ///16-bit RGBA 5551 format, [15:0] R:G:B:A 5:5:5:1 little endian - pub const RGBA5551: u32 = 892420434u32; - ///16-bit BGRA 5551 format, [15:0] B:G:R:A 5:5:5:1 little endian - pub const BGRA5551: u32 = 892420418u32; - ///16-bit RGB 565 format, [15:0] R:G:B 5:6:5 little endian - pub const RGB565: u32 = 909199186u32; - ///16-bit BGR 565 format, [15:0] B:G:R 5:6:5 little endian - pub const BGR565: u32 = 909199170u32; - ///24-bit RGB format, [23:0] R:G:B little endian - pub const RGB888: u32 = 875710290u32; - ///24-bit BGR format, [23:0] B:G:R little endian - pub const BGR888: u32 = 875710274u32; - ///32-bit xBGR format, [31:0] x:B:G:R 8:8:8:8 little endian - pub const XBGR8888: u32 = 875709016u32; - ///32-bit RGBx format, [31:0] R:G:B:x 8:8:8:8 little endian - pub const RGBX8888: u32 = 875714642u32; - ///32-bit BGRx format, [31:0] B:G:R:x 8:8:8:8 little endian - pub const BGRX8888: u32 = 875714626u32; - ///32-bit ABGR format, [31:0] A:B:G:R 8:8:8:8 little endian - pub const ABGR8888: u32 = 875708993u32; - ///32-bit RGBA format, [31:0] R:G:B:A 8:8:8:8 little endian - pub const RGBA8888: u32 = 875708754u32; - ///32-bit BGRA format, [31:0] B:G:R:A 8:8:8:8 little endian - pub const BGRA8888: u32 = 875708738u32; - ///32-bit xRGB format, [31:0] x:R:G:B 2:10:10:10 little endian - pub const XRGB2101010: u32 = 808669784u32; - ///32-bit xBGR format, [31:0] x:B:G:R 2:10:10:10 little endian - pub const XBGR2101010: u32 = 808665688u32; - ///32-bit RGBx format, [31:0] R:G:B:x 10:10:10:2 little endian - pub const RGBX1010102: u32 = 808671314u32; - ///32-bit BGRx format, [31:0] B:G:R:x 10:10:10:2 little endian - pub const BGRX1010102: u32 = 808671298u32; - ///32-bit ARGB format, [31:0] A:R:G:B 2:10:10:10 little endian - pub const ARGB2101010: u32 = 808669761u32; - ///32-bit ABGR format, [31:0] A:B:G:R 2:10:10:10 little endian - pub const ABGR2101010: u32 = 808665665u32; - ///32-bit RGBA format, [31:0] R:G:B:A 10:10:10:2 little endian - pub const RGBA1010102: u32 = 808665426u32; - ///32-bit BGRA format, [31:0] B:G:R:A 10:10:10:2 little endian - pub const BGRA1010102: u32 = 808665410u32; - ///packed YCbCr format, [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian - pub const YUYV: u32 = 1448695129u32; - ///packed YCbCr format, [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian - pub const YVYU: u32 = 1431918169u32; - ///packed YCbCr format, [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian - pub const UYVY: u32 = 1498831189u32; - ///packed YCbCr format, [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian - pub const VYUY: u32 = 1498765654u32; - ///packed AYCbCr format, [31:0] A:Y:Cb:Cr 8:8:8:8 little endian - pub const AYUV: u32 = 1448433985u32; - ///2 plane YCbCr Cr:Cb format, 2x2 subsampled Cr:Cb plane - pub const NV12: u32 = 842094158u32; - ///2 plane YCbCr Cb:Cr format, 2x2 subsampled Cb:Cr plane - pub const NV21: u32 = 825382478u32; - ///2 plane YCbCr Cr:Cb format, 2x1 subsampled Cr:Cb plane - pub const NV16: u32 = 909203022u32; - ///2 plane YCbCr Cb:Cr format, 2x1 subsampled Cb:Cr plane - pub const NV61: u32 = 825644622u32; - ///3 plane YCbCr format, 4x4 subsampled Cb (1) and Cr (2) planes - pub const YUV410: u32 = 961959257u32; - ///3 plane YCbCr format, 4x4 subsampled Cr (1) and Cb (2) planes - pub const YVU410: u32 = 961893977u32; - ///3 plane YCbCr format, 4x1 subsampled Cb (1) and Cr (2) planes - pub const YUV411: u32 = 825316697u32; - ///3 plane YCbCr format, 4x1 subsampled Cr (1) and Cb (2) planes - pub const YVU411: u32 = 825316953u32; - ///3 plane YCbCr format, 2x2 subsampled Cb (1) and Cr (2) planes - pub const YUV420: u32 = 842093913u32; - ///3 plane YCbCr format, 2x2 subsampled Cr (1) and Cb (2) planes - pub const YVU420: u32 = 842094169u32; - ///3 plane YCbCr format, 2x1 subsampled Cb (1) and Cr (2) planes - pub const YUV422: u32 = 909202777u32; - ///3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes - pub const YVU422: u32 = 909203033u32; - ///3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes - pub const YUV444: u32 = 875713881u32; - ///3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes - pub const YVU444: u32 = 875714137u32; - ///[7:0] R - pub const R8: u32 = 538982482u32; - ///[15:0] R little endian - pub const R16: u32 = 540422482u32; - ///[15:0] R:G 8:8 little endian - pub const RG88: u32 = 943212370u32; - ///[15:0] G:R 8:8 little endian - pub const GR88: u32 = 943215175u32; - ///[31:0] R:G 16:16 little endian - pub const RG1616: u32 = 842221394u32; - ///[31:0] G:R 16:16 little endian - pub const GR1616: u32 = 842224199u32; - ///[63:0] x:R:G:B 16:16:16:16 little endian - pub const XRGB16161616F: u32 = 1211388504u32; - ///[63:0] x:B:G:R 16:16:16:16 little endian - pub const XBGR16161616F: u32 = 1211384408u32; - ///[63:0] A:R:G:B 16:16:16:16 little endian - pub const ARGB16161616F: u32 = 1211388481u32; - ///[63:0] A:B:G:R 16:16:16:16 little endian - pub const ABGR16161616F: u32 = 1211384385u32; - ///[31:0] X:Y:Cb:Cr 8:8:8:8 little endian - pub const XYUV8888: u32 = 1448434008u32; - ///[23:0] Cr:Cb:Y 8:8:8 little endian - pub const VUY888: u32 = 875713878u32; - ///Y followed by U then V, 10:10:10. Non-linear modifier only - pub const VUY101010: u32 = 808670550u32; - ///[63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels - pub const Y210: u32 = 808530521u32; - ///[63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels - pub const Y212: u32 = 842084953u32; - ///[63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels - pub const Y216: u32 = 909193817u32; - ///[31:0] A:Cr:Y:Cb 2:10:10:10 little endian - pub const Y410: u32 = 808531033u32; - ///[63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian - pub const Y412: u32 = 842085465u32; - ///[63:0] A:Cr:Y:Cb 16:16:16:16 little endian - pub const Y416: u32 = 909194329u32; - ///[31:0] X:Cr:Y:Cb 2:10:10:10 little endian - pub const XVYU2101010: u32 = 808670808u32; - ///[63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian - pub const XVYU12_16161616: u32 = 909334104u32; - ///[63:0] X:Cr:Y:Cb 16:16:16:16 little endian - pub const XVYU16161616: u32 = 942954072u32; - ///[63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 - /// little endian - pub const Y0L0: u32 = 810299481u32; - ///[63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 - /// little endian - pub const X0L0: u32 = 810299480u32; - ///[63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian - pub const Y0L2: u32 = 843853913u32; - ///[63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian - pub const X0L2: u32 = 843853912u32; - pub const YUV420_8BIT: u32 = 942691673u32; - pub const YUV420_10BIT: u32 = 808539481u32; - pub const XRGB8888_A8: u32 = 943805016u32; - pub const XBGR8888_A8: u32 = 943800920u32; - pub const RGBX8888_A8: u32 = 943806546u32; - pub const BGRX8888_A8: u32 = 943806530u32; - pub const RGB888_A8: u32 = 943798354u32; - pub const BGR888_A8: u32 = 943798338u32; - pub const RGB565_A8: u32 = 943797586u32; - pub const BGR565_A8: u32 = 943797570u32; - ///non-subsampled Cr:Cb plane - pub const NV24: u32 = 875714126u32; - ///non-subsampled Cb:Cr plane - pub const NV42: u32 = 842290766u32; - ///2x1 subsampled Cr:Cb plane, 10 bit per channel - pub const P210: u32 = 808530512u32; - ///2x2 subsampled Cr:Cb plane 10 bits per channel - pub const P010: u32 = 808530000u32; - ///2x2 subsampled Cr:Cb plane 12 bits per channel - pub const P012: u32 = 842084432u32; - ///2x2 subsampled Cr:Cb plane 16 bits per channel - pub const P016: u32 = 909193296u32; - ///[63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian - pub const AXBXGXRX106106106106: u32 = 808534593u32; - ///2x2 subsampled Cr:Cb plane - pub const NV15: u32 = 892425806u32; - pub const Q410: u32 = 808531025u32; - pub const Q401: u32 = 825242705u32; - ///[63:0] x:R:G:B 16:16:16:16 little endian - pub const XRGB16161616: u32 = 942953048u32; - ///[63:0] x:B:G:R 16:16:16:16 little endian - pub const XBGR16161616: u32 = 942948952u32; - ///[63:0] A:R:G:B 16:16:16:16 little endian - pub const ARGB16161616: u32 = 942953025u32; - ///[63:0] A:B:G:R 16:16:16:16 little endian - pub const ABGR16161616: u32 = 942948929u32; - } -} -///content for a wl_surface -/// -///A buffer provides the content for a wl_surface. Buffers are -///created through factory interfaces such as wl_shm, wp_linux_buffer_params -///(from the linux-dmabuf protocol extension) or similar. It has a width and -///a height and can be attached to a wl_surface, but the mechanism by which a -///client provides and updates the contents is defined by the buffer factory -///interface. -/// -///If the buffer uses a format that has an alpha channel, the alpha channel -///is assumed to be premultiplied in the color channels unless otherwise -///specified. -/// -///Note, because wl_buffer objects are created from multiple independent -///factory interfaces, the wl_buffer interface is frozen at version 1. -pub mod wl_buffer { - use super::*; - - pub trait EvHandler { - ///compositor releases buffer - /// - ///Sent when this wl_buffer is no longer used by the compositor. - ///The client is now free to reuse or destroy this buffer and its - ///backing storage. - /// - ///If a client receives a release event before the frame callback - ///requested in the same wl_surface.commit that attaches this - ///wl_buffer to a surface, then the client is immediately free to - ///reuse the buffer and its backing storage, and does not need a - ///second buffer for the next surface content update. Typically - ///this is possible, when the compositor maintains a copy of the - ///wl_surface contents, e.g. as a GL texture. This is an important - ///optimization for GL(ES) compositors with wl_shm clients. - fn release(&mut self, sender_id: ObjectId); - } - - pub fn event(state: &mut T, wire_msg: WireMsg, _payload: WaylandPayload) { - match wire_msg.op() { - 0 => state.release(wire_msg.sender_id()), - e => log::error!("unrecognized event opcode: {e} for interface wl_buffer"), - } - } - ///Requests for this interface - pub mod req { - use super::*; - ///destroy a buffer - /// - ///Destroy a buffer. If and how you need to release the backing - ///storage is defined by the buffer factory interface. - /// - ///For possible side-effects to a surface, see wl_surface.attach. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.send() - } - } -} -///an onscreen surface -/// -///A surface is a rectangular area that may be displayed on zero -///or more outputs, and shown any number of times at the compositor's -///discretion. They can present wl_buffers, receive user input, and -///define a local coordinate system. -/// -///The size of a surface (and relative positions on it) is described -///in surface-local coordinates, which may differ from the buffer -///coordinates of the pixel content, in case a buffer_transform -///or a buffer_scale is used. -/// -///A surface without a "role" is fairly useless: a compositor does -///not know where, when or how to present it. The role is the -///purpose of a wl_surface. Examples of roles are a cursor for a -///pointer (as set by wl_pointer.set_cursor), a drag icon -///(wl_data_device.start_drag), a sub-surface -///(wl_subcompositor.get_subsurface), and a window as defined by a -///shell protocol (e.g. wl_shell.get_shell_surface). -/// -///A surface can have only one role at a time. Initially a -///wl_surface does not have a role. Once a wl_surface is given a -///role, it is set permanently for the whole lifetime of the -///wl_surface object. Giving the current role again is allowed, -///unless explicitly forbidden by the relevant interface -///specification. -/// -///Surface roles are given by requests in other interfaces such as -///wl_pointer.set_cursor. The request should explicitly mention -///that this request gives a role to a wl_surface. Often, this -///request also creates a new protocol object that represents the -///role and adds additional functionality to wl_surface. When a -///client wants to destroy a wl_surface, they must destroy this role -///object before the wl_surface, otherwise a defunct_role_object error is -///sent. -/// -///Destroying the role object does not remove the role from the -///wl_surface, but it may stop the wl_surface from "playing the role". -///For instance, if a wl_subsurface object is destroyed, the wl_surface -///it was created for will be unmapped and forget its position and -///z-order. It is allowed to create a wl_subsurface for the same -///wl_surface again, but it is not allowed to use the wl_surface as -///a cursor (cursor is a different role than sub-surface, and role -///switching is not allowed). -pub mod wl_surface { - use super::*; - - pub trait EvHandler { - ///surface enters an output - /// - ///This is emitted whenever a surface's creation, movement, or resizing - ///results in some part of it being within the scanout region of an - ///output. - /// - ///Note that a surface may be overlapping with zero or more outputs. - fn enter(&mut self, sender_id: ObjectId, output: ObjectId); - ///surface leaves an output - /// - ///This is emitted whenever a surface's creation, movement, or resizing - ///results in it no longer having any part of it within the scanout region - ///of an output. - /// - ///Clients should not use the number of outputs the surface is on for frame - ///throttling purposes. The surface might be hidden even if no leave event - ///has been sent, and the compositor might expect new surface content - ///updates even if no enter event has been sent. The frame event should be - ///used instead. - fn leave(&mut self, sender_id: ObjectId, output: ObjectId); - ///preferred buffer scale for the surface - /// - ///This event indicates the preferred buffer scale for this surface. It is - ///sent whenever the compositor's preference changes. - /// - ///It is intended that scaling aware clients use this event to scale their - ///content and use wl_surface.set_buffer_scale to indicate the scale they - ///have rendered with. This allows clients to supply a higher detail - ///buffer. - fn preferred_buffer_scale(&mut self, sender_id: ObjectId, factor: i32); - ///preferred buffer transform for the surface - /// - ///This event indicates the preferred buffer transform for this surface. - ///It is sent whenever the compositor's preference changes. - /// - ///It is intended that transform aware clients use this event to apply the - ///transform to their content and use wl_surface.set_buffer_transform to - ///indicate the transform they have rendered with. - fn preferred_buffer_transform(&mut self, sender_id: ObjectId, transform: u32); - } - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let output = wire_msg.next_object(&payload).unwrap(); - state.enter(wire_msg.sender_id(), output); - } - 1 => { - let output = wire_msg.next_object(&payload).unwrap(); - state.leave(wire_msg.sender_id(), output); - } - 2 => { - let factor = wire_msg.next_i32(&payload); - state.preferred_buffer_scale(wire_msg.sender_id(), factor); - } - 3 => { - let transform = wire_msg.next_u32(&payload); - state.preferred_buffer_transform(wire_msg.sender_id(), transform); - } - e => log::error!("unrecognized event opcode: {e} for interface wl_surface"), - } - } - ///Requests for this interface - pub mod req { - use super::*; - ///delete surface - /// - ///Deletes the surface and invalidates its object ID. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.send() - } - ///set the surface contents - /// - ///Set a buffer as the content of this surface. - /// - ///The new size of the surface is calculated based on the buffer - ///size transformed by the inverse buffer_transform and the - ///inverse buffer_scale. This means that at commit time the supplied - ///buffer size must be an integer multiple of the buffer_scale. If - ///that's not the case, an invalid_size error is sent. - /// - ///The x and y arguments specify the location of the new pending - ///buffer's upper left corner, relative to the current buffer's upper - ///left corner, in surface-local coordinates. In other words, the - ///x and y, combined with the new surface size define in which - ///directions the surface's size changes. Setting anything other than 0 - ///as x and y arguments is discouraged, and should instead be replaced - ///with using the separate wl_surface.offset request. - /// - ///When the bound wl_surface version is 5 or higher, passing any - ///non-zero x or y is a protocol violation, and will result in an - ///'invalid_offset' error being raised. The x and y arguments are ignored - ///and do not change the pending state. To achieve equivalent semantics, - ///use wl_surface.offset. - /// - ///Surface contents are double-buffered state, see wl_surface.commit. - /// - ///The initial surface contents are void; there is no content. - ///wl_surface.attach assigns the given wl_buffer as the pending - ///wl_buffer. wl_surface.commit makes the pending wl_buffer the new - ///surface contents, and the size of the surface becomes the size - ///calculated from the wl_buffer, as described above. After commit, - ///there is no pending buffer until the next attach. - /// - ///Committing a pending wl_buffer allows the compositor to read the - ///pixels in the wl_buffer. The compositor may access the pixels at - ///any time after the wl_surface.commit request. When the compositor - ///will not access the pixels anymore, it will send the - ///wl_buffer.release event. Only after receiving wl_buffer.release, - ///the client may reuse the wl_buffer. A wl_buffer that has been - ///attached and then replaced by another attach instead of committed - ///will not receive a release event, and is not used by the - ///compositor. - /// - ///If a pending wl_buffer has been committed to more than one wl_surface, - ///the delivery of wl_buffer.release events becomes undefined. A well - ///behaved client should not rely on wl_buffer.release events in this - ///case. Alternatively, a client could create multiple wl_buffer objects - ///from the same backing storage or use wp_linux_buffer_release. - /// - ///Destroying the wl_buffer after wl_buffer.release does not change - ///the surface contents. Destroying the wl_buffer before wl_buffer.release - ///is allowed as long as the underlying buffer storage isn't re-used (this - ///can happen e.g. on client process termination). However, if the client - ///destroys the wl_buffer before receiving the wl_buffer.release event and - ///mutates the underlying buffer storage, the surface contents become - ///undefined immediately. - /// - ///If wl_surface.attach is sent with a NULL wl_buffer, the - ///following wl_surface.commit will remove the surface content. - pub fn attach( - sender_id: ObjectId, - buffer: Option, - x: i32, - y: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 1); - wire_msg_builder.add_object(buffer); - wire_msg_builder.add_i32(x); - wire_msg_builder.add_i32(y); - wire_msg_builder.send() - } - ///mark part of the surface damaged - /// - ///This request is used to describe the regions where the pending - ///buffer is different from the current surface contents, and where - ///the surface therefore needs to be repainted. The compositor - ///ignores the parts of the damage that fall outside of the surface. - /// - ///Damage is double-buffered state, see wl_surface.commit. - /// - ///The damage rectangle is specified in surface-local coordinates, - ///where x and y specify the upper left corner of the damage rectangle. - /// - ///The initial value for pending damage is empty: no damage. - ///wl_surface.damage adds pending damage: the new pending damage - ///is the union of old pending damage and the given rectangle. - /// - ///wl_surface.commit assigns pending damage as the current damage, - ///and clears pending damage. The server will clear the current - ///damage as it repaints the surface. - /// - ///Note! New clients should not use this request. Instead damage can be - ///posted with wl_surface.damage_buffer which uses buffer coordinates - ///instead of surface coordinates. - pub fn damage( - sender_id: ObjectId, - x: i32, - y: i32, - width: i32, - height: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 2); - wire_msg_builder.add_i32(x); - wire_msg_builder.add_i32(y); - wire_msg_builder.add_i32(width); - wire_msg_builder.add_i32(height); - wire_msg_builder.send() - } - ///request a frame throttling hint - /// - ///Request a notification when it is a good time to start drawing a new - ///frame, by creating a frame callback. This is useful for throttling - ///redrawing operations, and driving animations. - /// - ///When a client is animating on a wl_surface, it can use the 'frame' - ///request to get notified when it is a good time to draw and commit the - ///next frame of animation. If the client commits an update earlier than - ///that, it is likely that some updates will not make it to the display, - ///and the client is wasting resources by drawing too often. - /// - ///The frame request will take effect on the next wl_surface.commit. - ///The notification will only be posted for one frame unless - ///requested again. For a wl_surface, the notifications are posted in - ///the order the frame requests were committed. - /// - ///The server must send the notifications so that a client - ///will not send excessive updates, while still allowing - ///the highest possible update rate for clients that wait for the reply - ///before drawing again. The server should give some time for the client - ///to draw and commit after sending the frame callback events to let it - ///hit the next output refresh. - /// - ///A server should avoid signaling the frame callbacks if the - ///surface is not visible in any way, e.g. the surface is off-screen, - ///or completely obscured by other opaque surfaces. - /// - ///The object returned by this request will be destroyed by the - ///compositor after the callback is fired and as such the client must not - ///attempt to use it after that point. - /// - ///The callback_data passed in the callback is the current time, in - ///milliseconds, with an undefined base. - pub fn frame(sender_id: ObjectId, callback: ObjectId) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 3); - wire_msg_builder.add_new_specified_id(callback); - wire_msg_builder.send() - } - ///set opaque region - /// - ///This request sets the region of the surface that contains - ///opaque content. - /// - ///The opaque region is an optimization hint for the compositor - ///that lets it optimize the redrawing of content behind opaque - ///regions. Setting an opaque region is not required for correct - ///behaviour, but marking transparent content as opaque will result - ///in repaint artifacts. - /// - ///The opaque region is specified in surface-local coordinates. - /// - ///The compositor ignores the parts of the opaque region that fall - ///outside of the surface. - /// - ///Opaque region is double-buffered state, see wl_surface.commit. - /// - ///wl_surface.set_opaque_region changes the pending opaque region. - ///wl_surface.commit copies the pending region to the current region. - ///Otherwise, the pending and current regions are never changed. - /// - ///The initial value for an opaque region is empty. Setting the pending - ///opaque region has copy semantics, and the wl_region object can be - ///destroyed immediately. A NULL wl_region causes the pending opaque - ///region to be set to empty. - pub fn set_opaque_region( - sender_id: ObjectId, - region: Option, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 4); - wire_msg_builder.add_object(region); - wire_msg_builder.send() - } - ///set input region - /// - ///This request sets the region of the surface that can receive - ///pointer and touch events. - /// - ///Input events happening outside of this region will try the next - ///surface in the server surface stack. The compositor ignores the - ///parts of the input region that fall outside of the surface. - /// - ///The input region is specified in surface-local coordinates. - /// - ///Input region is double-buffered state, see wl_surface.commit. - /// - ///wl_surface.set_input_region changes the pending input region. - ///wl_surface.commit copies the pending region to the current region. - ///Otherwise the pending and current regions are never changed, - ///except cursor and icon surfaces are special cases, see - ///wl_pointer.set_cursor and wl_data_device.start_drag. - /// - ///The initial value for an input region is infinite. That means the - ///whole surface will accept input. Setting the pending input region - ///has copy semantics, and the wl_region object can be destroyed - ///immediately. A NULL wl_region causes the input region to be set - ///to infinite. - pub fn set_input_region( - sender_id: ObjectId, - region: Option, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 5); - wire_msg_builder.add_object(region); - wire_msg_builder.send() - } - ///commit pending surface state - /// - ///Surface state (input, opaque, and damage regions, attached buffers, - ///etc.) is double-buffered. Protocol requests modify the pending state, - ///as opposed to the current state in use by the compositor. A commit - ///request atomically applies all pending state, replacing the current - ///state. After commit, the new pending state is as documented for each - ///related request. - /// - ///On commit, a pending wl_buffer is applied first, and all other state - ///second. This means that all coordinates in double-buffered state are - ///relative to the new wl_buffer coming into use, except for - ///wl_surface.attach itself. If there is no pending wl_buffer, the - ///coordinates are relative to the current surface contents. - /// - ///All requests that need a commit to become effective are documented - ///to affect double-buffered state. - /// - ///Other interfaces may add further double-buffered surface state. - pub fn commit(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 6); - wire_msg_builder.send() - } - ///sets the buffer transformation - /// - ///This request sets an optional transformation on how the compositor - ///interprets the contents of the buffer attached to the surface. The - ///accepted values for the transform parameter are the values for - ///wl_output.transform. - /// - ///Buffer transform is double-buffered state, see wl_surface.commit. - /// - ///A newly created surface has its buffer transformation set to normal. - /// - ///wl_surface.set_buffer_transform changes the pending buffer - ///transformation. wl_surface.commit copies the pending buffer - ///transformation to the current one. Otherwise, the pending and current - ///values are never changed. - /// - ///The purpose of this request is to allow clients to render content - ///according to the output transform, thus permitting the compositor to - ///use certain optimizations even if the display is rotated. Using - ///hardware overlays and scanning out a client buffer for fullscreen - ///surfaces are examples of such optimizations. Those optimizations are - ///highly dependent on the compositor implementation, so the use of this - ///request should be considered on a case-by-case basis. - /// - ///Note that if the transform value includes 90 or 270 degree rotation, - ///the width of the buffer will become the surface height and the height - ///of the buffer will become the surface width. - /// - ///If transform is not one of the values from the - ///wl_output.transform enum the invalid_transform protocol error - ///is raised. - pub fn set_buffer_transform(sender_id: ObjectId, transform: i32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 7); - wire_msg_builder.add_i32(transform); - wire_msg_builder.send() - } - ///sets the buffer scaling factor - /// - ///This request sets an optional scaling factor on how the compositor - ///interprets the contents of the buffer attached to the window. - /// - ///Buffer scale is double-buffered state, see wl_surface.commit. - /// - ///A newly created surface has its buffer scale set to 1. - /// - ///wl_surface.set_buffer_scale changes the pending buffer scale. - ///wl_surface.commit copies the pending buffer scale to the current one. - ///Otherwise, the pending and current values are never changed. - /// - ///The purpose of this request is to allow clients to supply higher - ///resolution buffer data for use on high resolution outputs. It is - ///intended that you pick the same buffer scale as the scale of the - ///output that the surface is displayed on. This means the compositor - ///can avoid scaling when rendering the surface on that output. - /// - ///Note that if the scale is larger than 1, then you have to attach - ///a buffer that is larger (by a factor of scale in each dimension) - ///than the desired surface size. - /// - ///If scale is not positive the invalid_scale protocol error is - ///raised. - pub fn set_buffer_scale(sender_id: ObjectId, scale: i32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 8); - wire_msg_builder.add_i32(scale); - wire_msg_builder.send() - } - ///mark part of the surface damaged using buffer coordinates - /// - ///This request is used to describe the regions where the pending - ///buffer is different from the current surface contents, and where - ///the surface therefore needs to be repainted. The compositor - ///ignores the parts of the damage that fall outside of the surface. - /// - ///Damage is double-buffered state, see wl_surface.commit. - /// - ///The damage rectangle is specified in buffer coordinates, - ///where x and y specify the upper left corner of the damage rectangle. - /// - ///The initial value for pending damage is empty: no damage. - ///wl_surface.damage_buffer adds pending damage: the new pending - ///damage is the union of old pending damage and the given rectangle. - /// - ///wl_surface.commit assigns pending damage as the current damage, - ///and clears pending damage. The server will clear the current - ///damage as it repaints the surface. - /// - ///This request differs from wl_surface.damage in only one way - it - ///takes damage in buffer coordinates instead of surface-local - ///coordinates. While this generally is more intuitive than surface - ///coordinates, it is especially desirable when using wp_viewport - ///or when a drawing library (like EGL) is unaware of buffer scale - ///and buffer transform. - /// - ///Note: Because buffer transformation changes and damage requests may - ///be interleaved in the protocol stream, it is impossible to determine - ///the actual mapping between surface and buffer damage until - ///wl_surface.commit time. Therefore, compositors wishing to take both - ///kinds of damage into account will have to accumulate damage from the - ///two requests separately and only transform from one to the other - ///after receiving the wl_surface.commit. - pub fn damage_buffer( - sender_id: ObjectId, - x: i32, - y: i32, - width: i32, - height: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 9); - wire_msg_builder.add_i32(x); - wire_msg_builder.add_i32(y); - wire_msg_builder.add_i32(width); - wire_msg_builder.add_i32(height); - wire_msg_builder.send() - } - ///set the surface contents offset - /// - ///The x and y arguments specify the location of the new pending - ///buffer's upper left corner, relative to the current buffer's upper - ///left corner, in surface-local coordinates. In other words, the - ///x and y, combined with the new surface size define in which - ///directions the surface's size changes. - /// - ///Surface location offset is double-buffered state, see - ///wl_surface.commit. - /// - ///This request is semantically equivalent to and the replaces the x and y - ///arguments in the wl_surface.attach request in wl_surface versions prior - ///to 5. See wl_surface.attach for details. - pub fn offset(sender_id: ObjectId, x: i32, y: i32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 10); - wire_msg_builder.add_i32(x); - wire_msg_builder.add_i32(y); - wire_msg_builder.send() - } - } - pub mod error { - ///buffer scale value is invalid - pub const INVALID_SCALE: u32 = 0u32; - ///buffer transform value is invalid - pub const INVALID_TRANSFORM: u32 = 1u32; - ///buffer size is invalid - pub const INVALID_SIZE: u32 = 2u32; - ///buffer offset is invalid - pub const INVALID_OFFSET: u32 = 3u32; - ///surface was destroyed before its role object - pub const DEFUNCT_ROLE_OBJECT: u32 = 4u32; - } -} -///compositor output region -/// -///An output describes part of the compositor geometry. The -///compositor works in the 'compositor coordinate system' and an -///output corresponds to a rectangular area in that space that is -///actually visible. This typically corresponds to a monitor that -///displays part of the compositor space. This object is published -///as global during start up, or when a monitor is hotplugged. -pub mod wl_output { - use super::*; - - pub trait EvHandler { - ///properties of the output - /// - ///The geometry event describes geometric properties of the output. - ///The event is sent when binding to the output object and whenever - ///any of the properties change. - /// - ///The physical size can be set to zero if it doesn't make sense for this - ///output (e.g. for projectors or virtual outputs). - /// - ///The geometry event will be followed by a done event (starting from - ///version 2). - /// - ///Note: wl_output only advertises partial information about the output - ///position and identification. Some compositors, for instance those not - ///implementing a desktop-style output layout or those exposing virtual - ///outputs, might fake this information. Instead of using x and y, clients - ///should use xdg_output.logical_position. Instead of using make and model, - ///clients should use name and description. - #[allow(clippy::too_many_arguments)] - fn geometry( - &mut self, - sender_id: ObjectId, - x: i32, - y: i32, - physical_width: i32, - physical_height: i32, - subpixel: i32, - make: &str, - model: &str, - transform: i32, - ); - ///advertise available modes for the output - /// - ///The mode event describes an available mode for the output. - /// - ///The event is sent when binding to the output object and there - ///will always be one mode, the current mode. The event is sent - ///again if an output changes mode, for the mode that is now - ///current. In other words, the current mode is always the last - ///mode that was received with the current flag set. - /// - ///Non-current modes are deprecated. A compositor can decide to only - ///advertise the current mode and never send other modes. Clients - ///should not rely on non-current modes. - /// - ///The size of a mode is given in physical hardware units of - ///the output device. This is not necessarily the same as - ///the output size in the global compositor space. For instance, - ///the output may be scaled, as described in wl_output.scale, - ///or transformed, as described in wl_output.transform. Clients - ///willing to retrieve the output size in the global compositor - ///space should use xdg_output.logical_size instead. - /// - ///The vertical refresh rate can be set to zero if it doesn't make - ///sense for this output (e.g. for virtual outputs). - /// - ///The mode event will be followed by a done event (starting from - ///version 2). - /// - ///Clients should not use the refresh rate to schedule frames. Instead, - ///they should use the wl_surface.frame event or the presentation-time - ///protocol. - /// - ///Note: this information is not always meaningful for all outputs. Some - ///compositors, such as those exposing virtual outputs, might fake the - ///refresh rate or the size. - fn mode(&mut self, sender_id: ObjectId, flags: u32, width: i32, height: i32, refresh: i32); - ///sent all information about output - /// - ///This event is sent after all other properties have been - ///sent after binding to the output object and after any - ///other property changes done after that. This allows - ///changes to the output properties to be seen as - ///atomic, even if they happen via multiple events. - fn done(&mut self, sender_id: ObjectId); - ///output scaling properties - /// - ///This event contains scaling geometry information - ///that is not in the geometry event. It may be sent after - ///binding the output object or if the output scale changes - ///later. If it is not sent, the client should assume a - ///scale of 1. - /// - ///A scale larger than 1 means that the compositor will - ///automatically scale surface buffers by this amount - ///when rendering. This is used for very high resolution - ///displays where applications rendering at the native - ///resolution would be too small to be legible. - /// - ///It is intended that scaling aware clients track the - ///current output of a surface, and if it is on a scaled - ///output it should use wl_surface.set_buffer_scale with - ///the scale of the output. That way the compositor can - ///avoid scaling the surface, and the client can supply - ///a higher detail image. - /// - ///The scale event will be followed by a done event. - fn scale(&mut self, sender_id: ObjectId, factor: i32); - ///name of this output - /// - ///Many compositors will assign user-friendly names to their outputs, show - ///them to the user, allow the user to refer to an output, etc. The client - ///may wish to know this name as well to offer the user similar behaviors. - /// - ///The name is a UTF-8 string with no convention defined for its contents. - ///Each name is unique among all wl_output globals. The name is only - ///guaranteed to be unique for the compositor instance. - /// - ///The same output name is used for all clients for a given wl_output - ///global. Thus, the name can be shared across processes to refer to a - ///specific wl_output global. - /// - ///The name is not guaranteed to be persistent across sessions, thus cannot - ///be used to reliably identify an output in e.g. configuration files. - /// - ///Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do - ///not assume that the name is a reflection of an underlying DRM connector, - ///X11 connection, etc. - /// - ///The name event is sent after binding the output object. This event is - ///only sent once per output object, and the name does not change over the - ///lifetime of the wl_output global. - /// - ///Compositors may re-use the same output name if the wl_output global is - ///destroyed and re-created later. Compositors should avoid re-using the - ///same name if possible. - /// - ///The name event will be followed by a done event. - fn name(&mut self, sender_id: ObjectId, name: &str); - ///human-readable description of this output - /// - ///Many compositors can produce human-readable descriptions of their - ///outputs. The client may wish to know this description as well, e.g. for - ///output selection purposes. - /// - ///The description is a UTF-8 string with no convention defined for its - ///contents. The description is not guaranteed to be unique among all - ///wl_output globals. Examples might include 'Foocorp 11" Display' or - ///'Virtual X11 output via :1'. - /// - ///The description event is sent after binding the output object and - ///whenever the description changes. The description is optional, and may - ///not be sent at all. - /// - ///The description event will be followed by a done event. - fn description(&mut self, sender_id: ObjectId, description: &str); - } - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let x = wire_msg.next_i32(&payload); - let y = wire_msg.next_i32(&payload); - let physical_width = wire_msg.next_i32(&payload); - let physical_height = wire_msg.next_i32(&payload); - let subpixel = wire_msg.next_i32(&payload); - let make = wire_msg.next_string(&payload); - let model = wire_msg.next_string(&payload); - let transform = wire_msg.next_i32(&payload); - state.geometry( - wire_msg.sender_id(), - x, - y, - physical_width, - physical_height, - subpixel, - make, - model, - transform, - ); - } - 1 => { - let flags = wire_msg.next_u32(&payload); - let width = wire_msg.next_i32(&payload); - let height = wire_msg.next_i32(&payload); - let refresh = wire_msg.next_i32(&payload); - state.mode(wire_msg.sender_id(), flags, width, height, refresh); - } - 2 => { - state.done(wire_msg.sender_id()); - } - 3 => { - let factor = wire_msg.next_i32(&payload); - state.scale(wire_msg.sender_id(), factor); - } - 4 => { - let name = wire_msg.next_string(&payload); - state.name(wire_msg.sender_id(), name); - } - 5 => { - let description = wire_msg.next_string(&payload); - state.description(wire_msg.sender_id(), description); - } - e => log::error!("unrecognized event opcode: {e} for interface wl_output"), - } - } - - ///Requests for this interface - pub mod req { - use super::*; - ///release the output object - /// - ///Using this request a client can tell the server that it is not going to - ///use the output object anymore. - /// - ///THIS IS A DESTRUCTOR - pub fn release(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0u16); - wire_msg_builder.send() - } - } - ///subpixel geometry information - /// - ///This enumeration describes how the physical - ///pixels on an output are laid out. - pub mod subpixel { - ///unknown geometry - pub const UNKNOWN: u32 = 0u32; - ///no geometry - pub const NONE: u32 = 1u32; - ///horizontal RGB - pub const HORIZONTAL_RGB: u32 = 2u32; - ///horizontal BGR - pub const HORIZONTAL_BGR: u32 = 3u32; - ///vertical RGB - pub const VERTICAL_RGB: u32 = 4u32; - ///vertical BGR - pub const VERTICAL_BGR: u32 = 5u32; - } - ///transform from framebuffer to output - /// - ///This describes the transform that a compositor will apply to a - ///surface to compensate for the rotation or mirroring of an - ///output device. - /// - ///The flipped values correspond to an initial flip around a - ///vertical axis followed by rotation. - /// - ///The purpose is mainly to allow clients to render accordingly and - ///tell the compositor, so that for fullscreen surfaces, the - ///compositor will still be able to scan out directly from client - ///surfaces. - pub mod transform { - ///no transform - pub const NORMAL: u32 = 0u32; - ///90 degrees counter-clockwise - pub const _90: u32 = 1u32; - ///180 degrees counter-clockwise - pub const _180: u32 = 2u32; - ///270 degrees counter-clockwise - pub const _270: u32 = 3u32; - ///180 degree flip around a vertical axis - pub const FLIPPED: u32 = 4u32; - ///flip and rotate 90 degrees counter-clockwise - pub const FLIPPED_90: u32 = 5u32; - ///flip and rotate 180 degrees counter-clockwise - pub const FLIPPED_180: u32 = 6u32; - ///flip and rotate 270 degrees counter-clockwise - pub const FLIPPED_270: u32 = 7u32; - } - ///BITFIELD - ///mode information - /// - ///These flags describe properties of an output mode. - ///They are used in the flags bitfield of the mode event. - pub mod mode { - ///indicates this is the current mode - pub const CURRENT: u32 = 1u32; - ///indicates this is the preferred mode - pub const PREFERRED: u32 = 2u32; - } -} -///region interface -/// -///A region object describes an area. -/// -///Region objects are used to describe the opaque and input -///regions of a surface. -pub mod wl_region { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///destroy region - /// - ///Destroy the region. This will invalidate the object ID. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0u16); - wire_msg_builder.send() - } - ///add rectangle to region - /// - ///Add the specified rectangle to the region. - pub fn add( - sender_id: ObjectId, - x: i32, - y: i32, - width: i32, - height: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 1u16); - wire_msg_builder.add_i32(x); - wire_msg_builder.add_i32(y); - wire_msg_builder.add_i32(width); - wire_msg_builder.add_i32(height); - wire_msg_builder.send() - } - ///subtract rectangle from region - /// - ///Subtract the specified rectangle from the region. - pub fn subtract( - sender_id: ObjectId, - x: i32, - y: i32, - width: i32, - height: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 2u16); - wire_msg_builder.add_i32(x); - wire_msg_builder.add_i32(y); - wire_msg_builder.add_i32(width); - wire_msg_builder.add_i32(height); - wire_msg_builder.send() - } - } -} -///surface cropping and scaling -/// -///The global interface exposing surface cropping and scaling -///capabilities is used to instantiate an interface extension for a -///wl_surface object. This extended interface will then allow -///cropping and scaling the surface contents, effectively -///disconnecting the direct relationship between the buffer and the -///surface size. -pub mod wp_viewporter { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///unbind from the cropping and scaling interface - /// - ///Informs the server that the client will not be using this - ///protocol object anymore. This does not affect any other objects, - ///wp_viewport objects included. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy() -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(globals::WP_VIEWPORTER, 0); - wire_msg_builder.send() - } - ///extend surface interface for crop and scale - /// - ///Instantiate an interface extension for the given wl_surface to - ///crop and scale its content. If the given wl_surface already has - ///a wp_viewport object associated, the viewport_exists - ///protocol error is raised. - pub fn get_viewport(id: ObjectId, surface: ObjectId) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::WP_VIEWPORTER, 1); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.add_object(Some(surface)); - wire_msg_builder.send() - } - } - pub mod error { - ///the surface already has a viewport object associated - pub const VIEWPORT_EXISTS: u32 = 0u32; - } -} -///crop and scale interface to a wl_surface -/// -///An additional interface to a wl_surface object, which allows the -///client to specify the cropping and scaling of the surface -///contents. -/// -///This interface works with two concepts: the source rectangle (src_x, -///src_y, src_width, src_height), and the destination size (dst_width, -///dst_height). The contents of the source rectangle are scaled to the -///destination size, and content outside the source rectangle is ignored. -///This state is double-buffered, and is applied on the next -///wl_surface.commit. -/// -///The two parts of crop and scale state are independent: the source -///rectangle, and the destination size. Initially both are unset, that -///is, no scaling is applied. The whole of the current wl_buffer is -///used as the source, and the surface size is as defined in -///wl_surface.attach. -/// -///If the destination size is set, it causes the surface size to become -///dst_width, dst_height. The source (rectangle) is scaled to exactly -///this size. This overrides whatever the attached wl_buffer size is, -///unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface -///has no content and therefore no size. Otherwise, the size is always -///at least 1x1 in surface local coordinates. -/// -///If the source rectangle is set, it defines what area of the wl_buffer is -///taken as the source. If the source rectangle is set and the destination -///size is not set, then src_width and src_height must be integers, and the -///surface size becomes the source rectangle size. This results in cropping -///without scaling. If src_width or src_height are not integers and -///destination size is not set, the bad_size protocol error is raised when -///the surface state is applied. -/// -///The coordinate transformations from buffer pixel coordinates up to -///the surface-local coordinates happen in the following order: -/// 1. buffer_transform (wl_surface.set_buffer_transform) -/// 2. buffer_scale (wl_surface.set_buffer_scale) -/// 3. crop and scale (wp_viewport.set*) -/// -///This means, that the source rectangle coordinates of crop and scale -///are given in the coordinates after the buffer transform and scale, -///i.e. in the coordinates that would be the surface-local coordinates -///if the crop and scale was not applied. -/// -///If src_x or src_y are negative, the bad_value protocol error is raised. -///Otherwise, if the source rectangle is partially or completely outside of -///the non-NULL wl_buffer, then the out_of_buffer protocol error is raised -///when the surface state is applied. A NULL wl_buffer does not raise the -///out_of_buffer error. -/// -///If the wl_surface associated with the wp_viewport is destroyed, -///all wp_viewport requests except 'destroy' raise the protocol error -///no_surface. -/// -///If the wp_viewport object is destroyed, the crop and scale -///state is removed from the wl_surface. The change will be applied -///on the next wl_surface.commit. -pub mod wp_viewport { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///remove scaling and cropping from the surface - /// - ///The associated wl_surface's crop and scale state is removed. - ///The change is applied on the next wl_surface.commit. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.send() - } - ///set the source rectangle for cropping - /// - ///Set the source rectangle of the associated wl_surface. See - ///wp_viewport for the description, and relation to the wl_buffer - ///size. - /// - ///If all of x, y, width and height are -1.0, the source rectangle is - ///unset instead. Any other set of values where width or height are zero - ///or negative, or x or y are negative, raise the bad_value protocol - ///error. - /// - ///The crop and scale state is double-buffered state, and will be - ///applied on the next wl_surface.commit. - pub fn set_source( - sender_id: ObjectId, - x: WlFixed, - y: WlFixed, - width: WlFixed, - height: WlFixed, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 1); - wire_msg_builder.add_fixed(x); - wire_msg_builder.add_fixed(y); - wire_msg_builder.add_fixed(width); - wire_msg_builder.add_fixed(height); - wire_msg_builder.send() - } - ///set the surface size for scaling - /// - ///Set the destination size of the associated wl_surface. See - ///wp_viewport for the description, and relation to the wl_buffer - ///size. - /// - ///If width is -1 and height is -1, the destination size is unset - ///instead. Any other pair of values for width and height that - ///contains zero or negative values raises the bad_value protocol - ///error. - /// - ///The crop and scale state is double-buffered state, and will be - ///applied on the next wl_surface.commit. - pub fn set_destination( - sender_id: ObjectId, - - width: i32, - height: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 2); - wire_msg_builder.add_i32(width); - wire_msg_builder.add_i32(height); - wire_msg_builder.send() - } - } - pub mod error { - ///negative or zero values in width or height - pub const BAD_VALUE: u32 = 0u32; - ///destination size is not integer - pub const BAD_SIZE: u32 = 1u32; - ///source rectangle extends outside of the content area - pub const OUT_OF_BUFFER: u32 = 2u32; - ///the wl_surface was destroyed - pub const NO_SURFACE: u32 = 3u32; - } -} -///Protocol for requesting fractional surface scales -/// -///This protocol allows a compositor to suggest for surfaces to render at -///fractional scales. -/// -///A client can submit scaled content by utilizing wp_viewport. This is done by -///creating a wp_viewport object for the surface and setting the destination -///rectangle to the surface size before the scale factor is applied. -/// -///The buffer size is calculated by multiplying the surface size by the -///intended scale. -/// -///The wl_surface buffer scale should remain set to 1. -/// -///If a surface has a surface-local size of 100 px by 50 px and wishes to -///submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should -///be used and the wp_viewport destination rectangle should be 100 px by 50 px. -/// -///For toplevel surfaces, the size is rounded halfway away from zero. The -///rounding algorithm for subsurface position and size is not defined. -///fractional surface scale information -/// -///A global interface for requesting surfaces to use fractional scales. -pub mod wp_fractional_scale_manager_v1 { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///unbind the fractional surface scale interface - /// - ///Informs the server that the client will not be using this protocol - ///object anymore. This does not affect any other objects, - ///wp_fractional_scale_v1 objects included. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.send() - } - ///extend surface interface for scale information - /// - ///Create an add-on object for the the wl_surface to let the compositor - ///request fractional scales. If the given wl_surface already has a - ///wp_fractional_scale_v1 object associated, the fractional_scale_exists - ///protocol error is raised. - pub fn get_fractional_scale( - sender_id: ObjectId, - id: ObjectId, - surface: ObjectId, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 1); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.add_object(Some(surface)); - wire_msg_builder.send() - } - } - pub mod error { - ///the surface already has a fractional_scale object associated - pub const FRACTIONAL_SCALE_EXISTS: u32 = 0u32; - } -} -///fractional scale interface to a wl_surface -/// -///An additional interface to a wl_surface object which allows the compositor -///to inform the client of the preferred scale. -pub mod wp_fractional_scale_v1 { - use super::*; - - pub trait EvHandler { - ///notify of new preferred scale - /// - ///Notification of a new preferred scale for this surface that the - ///compositor suggests that the client should use. - /// - ///The sent scale is the numerator of a fraction with a denominator of 120. - fn preferred_scale(&mut self, sender_id: ObjectId, scale: u32); - } - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let scale = wire_msg.next_u32(&payload); - state.preferred_scale(wire_msg.sender_id(), scale); - } - e => log::error!("unrecognized event opcode: {e} for interface wp_fractional_scale_v1"), - } - } - - ///Requests for this interface - pub mod req { - use super::*; - ///remove surface scale information for surface - /// - ///Destroy the fractional scale object. When this object is destroyed, - ///preferred_scale events will no longer be sent. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.send() - } - } -} -///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. -pub mod zwlr_layer_shell_v1 { - use super::*; - - ///Events for this interface - pub mod ev {} - ///Requests for this interface - pub mod req { - use super::*; - ///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. - pub fn get_layer_surface( - id: ObjectId, - surface: ObjectId, - output: Option, - layer: u32, - namespace: &str, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(globals::ZWLR_LAYER_SHELL_V1, 0); - wire_msg_builder.add_new_specified_id(id); - wire_msg_builder.add_object(Some(surface)); - wire_msg_builder.add_object(output); - wire_msg_builder.add_u32(layer); - wire_msg_builder.add_string(namespace); - wire_msg_builder.send() - } - ///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. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy() -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(globals::ZWLR_LAYER_SHELL_V1, 1); - wire_msg_builder.send() - } - } - pub mod error { - ///wl_surface has another role - pub const ROLE: u32 = 0u32; - ///layer value is invalid - pub const INVALID_LAYER: u32 = 1u32; - ///wl_surface has a buffer attached or committed - pub const ALREADY_CONSTRUCTED: u32 = 2u32; - } - ///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. - pub mod layer { - pub const BACKGROUND: u32 = 0u32; - pub const BOTTOM: u32 = 1u32; - pub const TOP: u32 = 2u32; - pub const OVERLAY: u32 = 3u32; - } -} -///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. -pub mod zwlr_layer_surface_v1 { - use super::*; - - pub trait EvHandler { - ///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. - fn configure(&mut self, sender_id: ObjectId, serial: u32, width: u32, height: u32); - ///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. - fn closed(&mut self, sender_id: ObjectId); - } - - pub fn event(state: &mut T, mut wire_msg: WireMsg, payload: WaylandPayload) { - match wire_msg.op() { - 0 => { - let serial = wire_msg.next_u32(&payload); - let width = wire_msg.next_u32(&payload); - let height = wire_msg.next_u32(&payload); - state.configure(wire_msg.sender_id(), serial, width, height); - } - 1 => state.closed(wire_msg.sender_id()), - e => log::error!("unrecognized event opcode: {e} for interface zwlr_layer_surface_v1"), - } - } - - ///Requests for this interface - pub mod req { - use super::*; - ///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. - pub fn set_size(sender_id: ObjectId, width: u32, height: u32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 0); - wire_msg_builder.add_u32(width); - wire_msg_builder.add_u32(height); - wire_msg_builder.send() - } - ///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. - pub fn set_anchor(sender_id: ObjectId, anchor: u32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 1); - wire_msg_builder.add_u32(anchor); - wire_msg_builder.send() - } - ///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. - pub fn set_exclusive_zone(sender_id: ObjectId, zone: i32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 2); - wire_msg_builder.add_i32(zone); - wire_msg_builder.send() - } - ///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. - pub fn set_margin( - sender_id: ObjectId, - top: i32, - right: i32, - bottom: i32, - left: i32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 3); - wire_msg_builder.add_i32(top); - wire_msg_builder.add_i32(right); - wire_msg_builder.add_i32(bottom); - wire_msg_builder.add_i32(left); - wire_msg_builder.send() - } - ///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. - pub fn set_keyboard_interactivity( - sender_id: ObjectId, - keyboard_interactivity: u32, - ) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 4); - wire_msg_builder.add_u32(keyboard_interactivity); - wire_msg_builder.send() - } - ///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. - pub fn get_popup(sender_id: ObjectId, popup: ObjectId) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 5); - wire_msg_builder.add_object(Some(popup)); - wire_msg_builder.send() - } - ///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. - pub fn ack_configure(sender_id: ObjectId, serial: u32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 6); - wire_msg_builder.add_u32(serial); - wire_msg_builder.send() - } - ///destroy the layer_surface - /// - ///This request destroys the layer surface. - /// - ///THIS IS A DESTRUCTOR - pub fn destroy(sender_id: ObjectId) -> rustix::io::Result<()> { - let wire_msg_builder = WireMsgBuilder::new(sender_id, 7); - wire_msg_builder.send() - } - ///change the layer of the surface - /// - ///Change the layer that the surface is rendered on. - /// - ///Layer is double-buffered, see wl_surface.commit. - pub fn set_layer(sender_id: ObjectId, layer: u32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 8); - wire_msg_builder.add_u32(layer); - wire_msg_builder.send() - } - ///set the edge the exclusive zone will be applied to - /// - ///Requests an edge for the exclusive zone to apply. The exclusive - ///edge will be automatically deduced from anchor points when possible, - ///but when the surface is anchored to a corner, it will be necessary - ///to set it explicitly to disambiguate, as it is not possible to deduce - ///which one of the two corner edges should be used. - /// - ///The edge must be one the surface is anchored to, otherwise the - ///invalid_exclusive_edge protocol error will be raised. - pub fn set_exclusive_edge(sender_id: ObjectId, edge: u32) -> rustix::io::Result<()> { - let mut wire_msg_builder = WireMsgBuilder::new(sender_id, 9); - wire_msg_builder.add_u32(edge); - wire_msg_builder.send() - } - } - ///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. - pub mod keyboard_interactivity { - ///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. - pub const NONE: u32 = 0u32; - ///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. - pub const EXCLUSIVE: u32 = 1u32; - ///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. - pub const ON_DEMAND: u32 = 2u32; - } - pub mod error { - ///provided surface state is invalid - pub const INVALID_SURFACE_STATE: u32 = 0u32; - ///size is invalid - pub const INVALID_SIZE: u32 = 1u32; - ///anchor bitfield is invalid - pub const INVALID_ANCHOR: u32 = 2u32; - ///keyboard interactivity is invalid - pub const INVALID_KEYBOARD_INTERACTIVITY: u32 = 3u32; - ///exclusive edge is invalid given the surface anchors - pub const INVALID_EXCLUSIVE_EDGE: u32 = 4u32; - } - ///BITFIELD - pub mod anchor { - ///the top edge of the anchor rectangle - pub const TOP: u32 = 1u32; - ///the bottom edge of the anchor rectangle - pub const BOTTOM: u32 = 2u32; - ///the left edge of the anchor rectangle - pub const LEFT: u32 = 4u32; - ///the right edge of the anchor rectangle - pub const RIGHT: u32 = 8u32; - } -} diff --git a/daemon/src/wayland/mod.rs b/daemon/src/wayland/mod.rs index 237cc0a5..472473ef 100644 --- a/daemon/src/wayland/mod.rs +++ b/daemon/src/wayland/mod.rs @@ -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 { - 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>, - /// 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 { - 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")); diff --git a/daemon/src/wayland/wire.rs b/daemon/src/wayland/wire.rs deleted file mode 100644 index e9154403..00000000 --- a/daemon/src/wayland/wire.rs +++ /dev/null @@ -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 { - 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, - fds: Vec, -} - -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) { - 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) { - 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) { - 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 for WlFixed { - #[must_use] - fn from(value: i32) -> Self { - Self(value * 256) - } -} - -impl From 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 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); - } -} diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 00000000..d62fd51e --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + 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. + + + + + 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. + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + This request destroys the layer surface. + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 00000000..c43ce963 --- /dev/null +++ b/tests/Cargo.toml @@ -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" diff --git a/tests/spell_check.rs b/tests/spell_check.rs index bf13da86..27a7fb07 100644 --- a/tests/spell_check.rs +++ b/tests/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", From 1b5646749af142338e00716c61918a59bbabbf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sat, 5 Apr 2025 18:24:06 -0300 Subject: [PATCH 2/5] fix most of CI * Update MRSV * Accept new Unicode License * Install libwayland-dev and wayland-protocols on CI * Curse nix-os for being weird and not working --- .github/workflows/check.yml | 1 + .github/workflows/nix-tests.yml | 1 + .github/workflows/test.yml | 3 +++ Cargo.toml | 2 +- deny.toml | 2 +- 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ea03ab00..aecbc99b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -17,6 +17,7 @@ jobs: - 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 diff --git a/.github/workflows/nix-tests.yml b/.github/workflows/nix-tests.yml index d5af0945..51971054 100644 --- a/.github/workflows/nix-tests.yml +++ b/.github/workflows/nix-tests.yml @@ -13,5 +13,6 @@ jobs: experimental-features = nix-command flakes access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - run: nix flake show + - run: nix-env -i nixpkgs#wayland nixpkgs#wayland-protocols - run: nix flake check --print-build-logs - run: nix build --print-build-logs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 07de14ec..340e6753 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,7 @@ jobs: - uses: mozilla-actions/sccache-action@v0.0.3 - 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 @@ -26,6 +27,7 @@ jobs: - uses: mozilla-actions/sccache-action@v0.0.3 - 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 @@ -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 diff --git a/Cargo.toml b/Cargo.toml index a3c24a5c..7f69141b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ version = "0.9.5-masterV3" authors = ["Leonardo Gibrowski Faé "] edition = "2021" license-file = "LICENSE" -rust-version = "1.82" +rust-version = "1.85" [workspace.dependencies] common = { path = "common" } diff --git a/deny.toml b/deny.toml index cbdacbb6..75c60118 100644 --- a/deny.toml +++ b/deny.toml @@ -20,7 +20,7 @@ allow = [ "BSD-3-Clause", "GPL-3.0", "MIT", - "Unicode-DFS-2016", + "Unicode-3.0" ] [sources] From 79b05263c9ccf1cb7fb39d9bdf61b19e0b250bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sat, 5 Apr 2025 19:04:59 -0300 Subject: [PATCH 3/5] update README with new build dependencies --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 66bc9fe2..a3580856 100644 --- a/README.md +++ b/README.md @@ -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: ``` From 50dbcfbb8f326b5839c25cfdc1ffc3e769a17498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sat, 5 Apr 2025 19:19:47 -0300 Subject: [PATCH 4/5] update .gitignore test_images path --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f599e6a2..ccae476f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ target/* completions/* -test_images/* +tests/test_images/* # Nix result From b415e6bc555ca646d4105699db9d9a8f58fe07bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 13 Apr 2025 11:45:17 -0300 Subject: [PATCH 5/5] update dependencies --- Cargo.lock | 42 +++++++++++++++++++++--------------------- daemon/Cargo.toml | 4 ++-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c88725e9..c8476376 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", @@ -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,9 +540,9 @@ checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litrs" @@ -564,9 +564,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -804,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" @@ -949,9 +949,9 @@ dependencies = [ [[package]] name = "waybackend" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fab4aa6b3b3fb33504a4e78848894f5878fead2de2e11380b26d8b923cd0ba8" +checksum = "76e6145c18559c8c104c6700bba2f2823c257ec6733a539674f0d54db822befb" dependencies = [ "bitflags 2.9.0", "log", @@ -960,9 +960,9 @@ dependencies = [ [[package]] name = "waybackend-scanner" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f641aeacf2d43eb3bae0f9b05b51719d27d973e11bd9a6a8f28d81798bdadf" +checksum = "c3f4b7476ffcc25cf0df86c80cd55b2244c74078cb90fb37b0deb83becbd251e" dependencies = [ "pkg-config", "proc-macro2", diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 94cd9bb4..c64cdf46 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] common = { workspace = true } -waybackend = { version = "0.2" } +waybackend = { version = "0.3" } log = { version = "0.4", default-features = false, features = [ "max_level_debug", "release_max_level_info", @@ -24,4 +24,4 @@ keyframe = "1.1" sd-notify = { version = "0.4" } [build-dependencies] -waybackend-scanner = { version = "0.2", features = ["build-script"] } +waybackend-scanner = { version = "0.3", features = ["build-script"] }