commit d6172b273f66dcd2cd5ee2f8599bcee1ef8f3902 Author: Julian Orth Date: Sun Jan 2 15:13:33 2022 +0100 autocommit 2022-01-02 15:13:33 CET diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9580f4c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.* +!.gitignore +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..fadd0fe1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,407 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "futures" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-executor" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" + +[[package]] +name = "futures-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + +[[package]] +name = "futures-task" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" + +[[package]] +name = "futures-util" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "i4" +version = "0.1.0" +dependencies = [ + "ahash", + "anyhow", + "env_logger", + "futures", + "log", + "thiserror", + "uapi", + "waker-fn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "syn" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "uapi" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c72d8c426678584cd311d3b8e9538778fd7d024de641b440850e458d50621fd" +dependencies = [ + "cc", + "cfg-if 0.1.10", + "libc", + "uapi-proc", +] + +[[package]] +name = "uapi-proc" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16121f38f4afe754d6726ed388f1fb3e669c4594a3d29e1bdab1e795a450921f" +dependencies = [ + "lazy_static", + "libc", + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..fc408409 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "i4" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +panic = "abort" + +[profile.dev] +panic = "abort" + +[dependencies] +uapi = "0.2.4" +thiserror = "1.0.30" +anyhow = "1.0.52" +ahash = "0.7.6" +log = "0.4.14" +env_logger = "0.9.0" +futures = "0.3.19" +waker-fn = "1.1.0" diff --git a/src/acceptor.rs b/src/acceptor.rs new file mode 100644 index 00000000..6eeb0816 --- /dev/null +++ b/src/acceptor.rs @@ -0,0 +1,134 @@ +use crate::event_loop::{EventLoopDispatcher, EventLoopError, EventLoopId}; +use crate::state::State; +use crate::wl_client::WlClientError; +use std::rc::Rc; +use thiserror::Error; +use uapi::{c, Errno, OwnedFd}; + +#[derive(Debug, Error)] +pub enum AcceptorError { + #[error("XDG_RUNTIME_DIR is not set")] + XrdNotSet, + #[error("XDG_RUNTIME_DIR is too long to form a unix socket address")] + XrdTooLong, + #[error("Could not create a wayland socket")] + SocketFailed(#[source] std::io::Error), + #[error("Could not start listening for incoming connections")] + ListenFailed(#[source] std::io::Error), + #[error("The wayland socket is in an error state")] + ErrorEvent, + #[error("Could not accept new connections")] + AcceptFailed(#[source] std::io::Error), + #[error("Could not spawn an event handler for a new connection")] + SpawnFailed(#[source] WlClientError), + #[error("Could not bind the socket to an address")] + BindFailed(#[source] std::io::Error), + #[error("All wayland addresses in the range 0..1000 are already in use")] + AddressesInUse, + #[error("The event loop caused an error")] + EventLoopError(#[from] EventLoopError), +} + +pub struct Acceptor { + _unlinker: Unlinker, + id: EventLoopId, + fd: OwnedFd, + global: Rc, +} + +struct Unlinker(String); + +impl Drop for Unlinker { + fn drop(&mut self) { + let _ = uapi::unlink(self.0.as_str()); + } +} + +fn socket_path(xrd: &str, id: u32) -> String { + format!("{}/wayland-{}", xrd, id) +} + +fn bind_socket(fd: i32, xdr: &str) -> Result { + let mut addr: c::sockaddr_un = uapi::pod_zeroed(); + addr.sun_family = c::AF_UNIX as _; + for i in 0..1000 { + let path = socket_path(xdr, i); + if path.len() + 1 > addr.sun_path.len() { + return Err(AcceptorError::XrdTooLong); + } + let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]); + sun_path[..path.len()].copy_from_slice(path.as_bytes()); + sun_path[path.len()] = 0; + match uapi::bind(fd, &addr) { + Ok(()) => return Ok(i), + Err(Errno(c::EADDRINUSE)) => { + log::warn!("Socket {} is already in use", path); + } + Err(e) => return Err(AcceptorError::BindFailed(e.into())), + } + } + Err(AcceptorError::AddressesInUse) +} + +impl Acceptor { + pub fn install(global: &Rc) -> Result<(), AcceptorError> { + let xrd = match std::env::var("XDG_RUNTIME_DIR") { + Ok(d) => d, + Err(_) => return Err(AcceptorError::XrdNotSet), + }; + let fd = match uapi::socket( + c::AF_UNIX, + c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC, + 0, + ) { + Ok(f) => f, + Err(e) => return Err(AcceptorError::SocketFailed(e.into())), + }; + let socket_id = bind_socket(fd.raw(), &xrd)?; + let socket_path = socket_path(&xrd, socket_id); + log::info!("bound to socket {}", socket_path); + let unlinker = Unlinker(socket_path); + if let Err(e) = uapi::listen(fd.raw(), 4096) { + return Err(AcceptorError::ListenFailed(e.into())); + } + let id = global.el.id()?; + let acc = Rc::new(Acceptor { + _unlinker: unlinker, + id, + fd, + global: global.clone(), + }); + global.el.insert(id, Some(acc.fd.raw()), c::EPOLLIN, acc)?; + Ok(()) + } +} + +impl EventLoopDispatcher for Acceptor { + fn dispatch(&self, events: i32) -> Result<(), Box> { + if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { + return Err(Box::new(AcceptorError::ErrorEvent)); + } + loop { + let fd = match uapi::accept4( + self.fd.raw(), + uapi::sockaddr_none_mut(), + c::SOCK_NONBLOCK | c::SOCK_CLOEXEC, + ) { + Ok((fd, _)) => fd, + Err(Errno(c::EAGAIN)) => break, + Err(e) => return Err(Box::new(AcceptorError::AcceptFailed(e.into()))), + }; + let id = self.global.clients.id(); + if let Err(e) = self.global.clients.spawn(id, &self.global, fd) { + return Err(Box::new(AcceptorError::SpawnFailed(e))); + } + } + Ok(()) + } +} + +impl Drop for Acceptor { + fn drop(&mut self) { + let _ = self.global.el.remove(self.id); + } +} diff --git a/src/async_engine.rs b/src/async_engine.rs new file mode 100644 index 00000000..b56b996b --- /dev/null +++ b/src/async_engine.rs @@ -0,0 +1,667 @@ +pub use crate::async_engine::yield_::Yield; +use crate::event_loop::{EventLoopError, EventLoopRef}; +use crate::utils::copyhashmap::CopyHashMap; +use crate::utils::numcell::NumCell; +use crate::wheel::{WheelError, WheelRef}; +pub use fd::AsyncFd; +use fd::AsyncFdData; +use queue::{DispatchQueue, Dispatcher}; +use std::cell::{Cell, RefCell}; +use std::future::Future; +use std::rc::Rc; +pub use task::SpawnedFuture; +use thiserror::Error; +pub use timeout::Timeout; +use timeout::TimeoutData; +use uapi::OwnedFd; + +#[derive(Debug, Error)] +pub enum AsyncError { + #[error("The timer wheel returned an error: {0}")] + WheelError(#[from] WheelError), + #[error("The event loop caused an error: {0}")] + EventLoopError(#[from] EventLoopError), +} + +pub struct AsyncEngine { + wheel: WheelRef, + el: EventLoopRef, + queue: Rc, + fds: CopyHashMap>, +} + +impl AsyncEngine { + pub fn new(el: &EventLoopRef, wheel: &WheelRef) -> Result { + let queue = Dispatcher::install(el)?; + Ok(Self { + wheel: wheel.clone(), + el: el.clone(), + queue, + fds: CopyHashMap::new(), + }) + } + + pub fn timeout(&self, ms: u64) -> Result { + let data = Rc::new(TimeoutData { + expired: Cell::new(false), + waker: RefCell::new(None), + }); + let id = self.wheel.id()?; + self.wheel.timeout(id, ms, data.clone())?; + Ok(Timeout { + id, + wheel: self.wheel.clone(), + data, + }) + } + + pub fn spawn + 'static>(&self, f: F) -> SpawnedFuture { + self.queue.spawn(f) + } + + pub fn fd(self: &Rc, fd: &Rc) -> Result { + let data = if let Some(afd) = self.fds.get(&fd.raw()) { + afd.ref_count.fetch_add(1); + afd + } else { + let id = self.el.id()?; + let afd = Rc::new(AsyncFdData { + ref_count: NumCell::new(1), + fd: fd.clone(), + id, + el: self.el.clone(), + write_registered: Cell::new(false), + read_registered: Cell::new(false), + readers: RefCell::new(vec![]), + writers: RefCell::new(vec![]), + erroneous: Cell::new(false), + }); + self.el.insert(id, Some(fd.raw()), 0, afd.clone())?; + afd + }; + Ok(AsyncFd { + engine: self.clone(), + data, + }) + } + + pub fn yield_now(&self) -> Yield { + Yield { + iteration: self.queue.iteration(), + queue: self.queue.clone(), + } + } +} + +mod yield_ { + use crate::async_engine::queue::DispatchQueue; + use std::future::Future; + use std::pin::Pin; + use std::rc::Rc; + use std::task::{Context, Poll}; + + pub struct Yield { + pub(super) iteration: u64, + pub(super) queue: Rc, + } + + impl Future for Yield { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.queue.iteration() > self.iteration { + Poll::Ready(()) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } +} + +mod timeout { + use crate::wheel::{WheelDispatcher, WheelId, WheelRef}; + use std::cell::{Cell, RefCell}; + use std::error::Error; + use std::future::Future; + use std::pin::Pin; + use std::rc::Rc; + use std::task::{Context, Poll, Waker}; + + pub(super) struct TimeoutData { + pub expired: Cell, + pub waker: RefCell>, + } + + impl WheelDispatcher for TimeoutData { + fn dispatch(self: Rc) -> Result<(), Box> { + self.expired.set(true); + if let Some(w) = self.waker.borrow_mut().take() { + w.wake(); + } + Ok(()) + } + } + + pub struct Timeout { + pub(super) id: WheelId, + pub(super) wheel: WheelRef, + pub(super) data: Rc, + } + + impl Future for Timeout { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.data.expired.get() { + Poll::Ready(()) + } else { + *self.data.waker.borrow_mut() = Some(cx.waker().clone()); + Poll::Pending + } + } + } + + impl Drop for Timeout { + fn drop(&mut self) { + self.wheel.remove(self.id); + } + } +} + +mod task { + use crate::async_engine::queue::DispatchQueue; + use std::future::Future; + use std::mem::ManuallyDrop; + use std::pin::Pin; + use std::ptr; + use std::rc::Rc; + use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + pub struct SpawnedFuture { + vtable: &'static SpawnedFutureVtable, + data: *mut u8, + } + + impl Future for SpawnedFuture { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { (self.vtable.poll)(self.data, cx) } + } + } + + impl Drop for SpawnedFuture { + fn drop(&mut self) { + unsafe { + (self.vtable.drop)(self.data); + } + } + } + + struct SpawnedFutureVTableProxy(T, F); + + impl> SpawnedFutureVTableProxy { + const VTABLE: &'static SpawnedFutureVtable = &SpawnedFutureVtable { + poll: Self::poll, + drop: Self::drop, + }; + + unsafe fn poll(data: *mut u8, ctx: &mut Context<'_>) -> Poll { + let task = &mut *(data as *mut Task); + if task.state & COMPLETED == 0 { + task.waker = Some(ctx.waker().clone()); + Poll::Pending + } else if task.state & EMPTIED == 0 { + task.state |= EMPTIED; + Poll::Ready(ptr::read(&mut *task.data.result)) + } else { + panic!("Future polled after it has already been emptied"); + } + } + + unsafe fn drop(data: *mut u8) { + let task = &mut *(data as *mut Task); + task.state |= CANCELLED; + if task.state & RUNNING == 0 { + task.drop_data(); + } + task.dec_ref_count(); + } + } + + struct SpawnedFutureVtable { + poll: unsafe fn(data: *mut u8, ctx: &mut Context<'_>) -> Poll, + drop: unsafe fn(data: *mut u8), + } + + union TaskData> { + result: ManuallyDrop, + future: ManuallyDrop, + } + + const RUNNING: usize = 1; + const RUN_AGAIN: usize = 2; + const COMPLETED: usize = 4; + const EMPTIED: usize = 8; + const CANCELLED: usize = 16; + + struct Task> { + ref_count: u64, + state: usize, + data: TaskData, + waker: Option, + queue: Rc, + } + + pub(super) struct Runnable { + data: *mut u8, + run: unsafe fn(data: *mut u8, run: bool), + } + + impl Runnable { + pub(super) fn run(self) { + let slf = ManuallyDrop::new(self); + unsafe { + (slf.run)(slf.data, true); + } + } + } + + impl Drop for Runnable { + fn drop(&mut self) { + unsafe { + (self.run)(self.data, false); + } + } + } + + impl DispatchQueue { + pub(super) fn spawn>(self: &Rc, f: F) -> SpawnedFuture { + let mut f = Box::new(Task { + ref_count: 1, + state: 0, + data: TaskData { + future: ManuallyDrop::new(f), + }, + waker: None, + queue: self.clone(), + }); + unsafe { + f.schedule_run(); + } + let f = Box::into_raw(f); + SpawnedFuture { + vtable: &SpawnedFutureVTableProxy::::VTABLE, + data: f as _, + } + } + } + + impl> Task { + const VTABLE: &'static RawWakerVTable = &RawWakerVTable::new( + Self::waker_clone, + Self::waker_wake, + Self::waker_wake_by_ref, + Self::waker_drop, + ); + + unsafe fn run_proxy(data: *mut u8, run: bool) { + let task = &mut *(data as *mut Self); + if run { + task.run(); + } + task.dec_ref_count(); + } + + unsafe fn dec_ref_count(&mut self) { + self.ref_count -= 1; + if self.ref_count == 0 { + Box::from_raw(self); + } + } + + unsafe fn inc_ref_count(&mut self) { + self.ref_count += 1; + } + + unsafe fn waker_clone(data: *const ()) -> RawWaker { + let task = &mut *(data as *mut Self); + task.inc_ref_count(); + RawWaker::new(data, Self::VTABLE) + } + + unsafe fn waker_wake(data: *const ()) { + Self::waker_wake_by_ref(data); + Self::waker_drop(data); + } + + unsafe fn waker_wake_by_ref(data: *const ()) { + let task = &mut *(data as *mut Self); + task.schedule_run(); + } + + unsafe fn waker_drop(data: *const ()) { + let task = &mut *(data as *mut Self); + task.dec_ref_count(); + } + + unsafe fn schedule_run(&mut self) { + if self.state & (COMPLETED | CANCELLED) == 0 { + if self.state & RUNNING == 0 { + self.state |= RUNNING; + self.inc_ref_count(); + let data = self as *mut _ as _; + self.queue.push(Runnable { + data, + run: Self::run_proxy, + }); + } else { + self.state |= RUN_AGAIN; + } + } + } + + unsafe fn run(&mut self) { + if self.state & CANCELLED == 0 { + self.inc_ref_count(); + let raw_waker = RawWaker::new(self as *const _ as _, &Self::VTABLE); + let waker = Waker::from_raw(raw_waker); + + let mut ctx = Context::from_waker(&waker); + if let Poll::Ready(d) = Pin::new_unchecked(&mut *self.data.future).poll(&mut ctx) { + ManuallyDrop::drop(&mut self.data.future); + ptr::write(&mut self.data.result, ManuallyDrop::new(d)); + self.state |= COMPLETED; + if let Some(waker) = self.waker.take() { + waker.wake(); + } + } + } + + self.state &= !RUNNING; + + if self.state & CANCELLED != 0 { + self.drop_data(); + } else if self.state & RUN_AGAIN != 0 { + self.state &= !RUN_AGAIN; + self.schedule_run() + } + } + + unsafe fn drop_data(&mut self) { + if self.state & COMPLETED == 0 { + ManuallyDrop::drop(&mut self.data.future); + } else if self.state & EMPTIED == 0 { + ManuallyDrop::drop(&mut self.data.result); + } + } + } +} + +mod queue { + use crate::async_engine::task::Runnable; + use crate::async_engine::AsyncError; + use crate::event_loop::{EventLoopDispatcher, EventLoopId, EventLoopRef}; + use crate::utils::numcell::NumCell; + use std::cell::{Cell, RefCell}; + use std::collections::VecDeque; + use std::error::Error; + use std::mem; + use std::rc::Rc; + + pub(super) struct Dispatcher { + queue: Rc, + stash: RefCell>, + } + + impl Dispatcher { + pub fn install(el: &EventLoopRef) -> Result, AsyncError> { + let id = el.id()?; + let queue = Rc::new(DispatchQueue { + id, + el: el.clone(), + dispatch_scheduled: Cell::new(false), + queue: RefCell::new(Default::default()), + iteration: Default::default(), + }); + let slf = Rc::new(Dispatcher { + queue: queue.clone(), + stash: RefCell::new(Default::default()), + }); + el.insert(id, None, 0, slf)?; + Ok(queue) + } + } + + impl EventLoopDispatcher for Dispatcher { + fn dispatch(&self, _events: i32) -> Result<(), Box> { + loop { + self.queue.iteration.fetch_add(1); + let mut stash = self.stash.borrow_mut(); + mem::swap(&mut *stash, &mut *self.queue.queue.borrow_mut()); + if stash.is_empty() { + break; + } + for runnable in stash.drain(..) { + runnable.run(); + } + } + self.queue.dispatch_scheduled.set(false); + Ok(()) + } + } + + impl Drop for Dispatcher { + fn drop(&mut self) { + let _ = self.queue.el.remove(self.queue.id); + mem::take(&mut *self.queue.queue.borrow_mut()); + } + } + + pub(super) struct DispatchQueue { + dispatch_scheduled: Cell, + id: EventLoopId, + el: EventLoopRef, + queue: RefCell>, + iteration: NumCell, + } + + impl DispatchQueue { + pub fn push(&self, runnable: Runnable) { + self.queue.borrow_mut().push_back(runnable); + if !self.dispatch_scheduled.get() { + let _ = self.el.schedule(self.id); + self.dispatch_scheduled.set(true); + } + } + + pub fn iteration(&self) -> u64 { + self.iteration.load() + } + } +} + +mod fd { + use crate::async_engine::{AsyncEngine, AsyncError}; + use crate::event_loop::{EventLoopDispatcher, EventLoopError, EventLoopId, EventLoopRef}; + use crate::utils::numcell::NumCell; + use std::cell::{Cell, RefCell}; + use std::error::Error; + use std::future::Future; + use std::pin::Pin; + use std::rc::Rc; + use std::task::{Context, Poll, Waker}; + use uapi::{c, OwnedFd}; + + type Queue = RefCell>)>>; + + pub(super) struct AsyncFdData { + pub(super) ref_count: NumCell, + pub(super) fd: Rc, + pub(super) id: EventLoopId, + pub(super) el: EventLoopRef, + pub(super) write_registered: Cell, + pub(super) read_registered: Cell, + pub(super) readers: Queue, + pub(super) writers: Queue, + pub(super) erroneous: Cell, + } + + impl AsyncFdData { + fn update_interests(&self) -> Result<(), EventLoopError> { + let mut events = 0; + if self.write_registered.get() { + events |= c::EPOLLOUT; + } + if self.read_registered.get() { + events |= c::EPOLLIN; + } + let res = self.el.modify(self.id, events); + if res.is_err() { + self.erroneous.set(true); + let _ = self.el.remove(self.id); + } + res + } + + fn poll( + &self, + woken: &Rc>, + cx: &mut Context<'_>, + registered: impl Fn(&AsyncFdData) -> &Cell, + queue: impl Fn(&AsyncFdData) -> &Queue, + ) -> Poll> { + if woken.get() || self.erroneous.get() { + return Poll::Ready(Ok(())); + } + if !registered(self).get() { + registered(self).set(true); + if let Err(e) = self.update_interests() { + return Poll::Ready(Err(AsyncError::EventLoopError(e))); + } + } + queue(self) + .borrow_mut() + .push((cx.waker().clone(), woken.clone())); + Poll::Pending + } + } + + impl EventLoopDispatcher for AsyncFdData { + fn dispatch(&self, events: i32) -> Result<(), Box> { + if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { + self.erroneous.set(true); + if let Err(e) = self.el.remove(self.id) { + return Err(Box::new(e)); + } + } + let mut woke_any = false; + if events & c::EPOLLIN != 0 || self.erroneous.get() { + let mut readers = self.readers.borrow_mut(); + woke_any |= !readers.is_empty(); + for (waker, woken) in readers.drain(..) { + woken.set(true); + waker.wake(); + } + } + if events & c::EPOLLOUT != 0 || self.erroneous.get() { + let mut writers = self.writers.borrow_mut(); + woke_any |= !writers.is_empty(); + for (waker, woken) in writers.drain(..) { + woken.set(true); + waker.wake(); + } + } + if !woke_any && !self.erroneous.get() { + self.read_registered.set(false); + self.write_registered.set(false); + if let Err(e) = self.update_interests() { + return Err(Box::new(e)); + } + } + Ok(()) + } + } + + impl Drop for AsyncFdData { + fn drop(&mut self) { + let _ = self.el.remove(self.id); + } + } + + pub struct AsyncFd { + pub(super) engine: Rc, + pub(super) data: Rc, + } + + impl Clone for AsyncFd { + fn clone(&self) -> Self { + self.data.ref_count.fetch_add(1); + Self { + engine: self.engine.clone(), + data: self.data.clone(), + } + } + } + + impl Drop for AsyncFd { + fn drop(&mut self) { + if self.data.ref_count.fetch_sub(1) == 1 { + self.engine.fds.remove(&self.data.fd.raw()); + } + } + } + + impl AsyncFd { + pub fn raw(&self) -> i32 { + self.data.fd.raw() + } + + pub fn eng(&self) -> &Rc { + &self.engine + } + + pub fn readable(&self) -> AsyncFdReadable { + AsyncFdReadable { + fd: self, + woken: Rc::new(Cell::new(false)), + } + } + + pub fn writable(&self) -> AsyncFdWritable { + AsyncFdWritable { + fd: self, + woken: Rc::new(Cell::new(false)), + } + } + } + + pub struct AsyncFdReadable<'a> { + fd: &'a AsyncFd, + woken: Rc>, + } + + impl<'a> Future for AsyncFdReadable<'a> { + type Output = Result<(), AsyncError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let data = &self.fd.data; + data.poll(&self.woken, cx, |d| &d.read_registered, |d| &d.readers) + } + } + + pub struct AsyncFdWritable<'a> { + fd: &'a AsyncFd, + woken: Rc>, + } + + impl<'a> Future for AsyncFdWritable<'a> { + type Output = Result<(), AsyncError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let data = &self.fd.data; + data.poll(&self.woken, cx, |d| &d.write_registered, |d| &d.writers) + } + } +} diff --git a/src/clientmem.rs b/src/clientmem.rs new file mode 100644 index 00000000..2a267e4a --- /dev/null +++ b/src/clientmem.rs @@ -0,0 +1,149 @@ +use std::cell::{Cell, UnsafeCell}; +use std::mem::MaybeUninit; +use std::sync::atomic::{compiler_fence, Ordering}; +use std::{mem, ptr}; +use thiserror::Error; +use uapi::c; +use uapi::c::raise; + +#[derive(Debug, Error)] +pub enum ClientMemError { + #[error("Could not install the sigbus handler")] + SigactionFailed(#[source] std::io::Error), + #[error("A SIGBUS occurred while accessing mapped memory")] + Sigbus, + #[error("mmap failed")] + MmapFailed(#[source] std::io::Error), +} + +pub struct ClientMem { + failed: Cell, + sigbus_impossible: bool, + data: *mut [Cell], +} + +impl ClientMem { + pub fn new(fd: i32, len: usize) -> Result { + let mut sigbus_impossible = false; + if let Ok(seals) = uapi::fcntl_get_seals(fd) { + if seals & c::F_SEAL_SHRINK != 0 { + if let Ok(stat) = uapi::fstat(fd) { + sigbus_impossible = stat.st_size as u64 >= len as u64; + } + } + } + let data = unsafe { + let data = c::mmap64( + ptr::null_mut(), + len, + c::PROT_READ | c::PROT_WRITE, + c::MAP_SHARED, + fd, + 0, + ); + if data == c::MAP_FAILED { + return Err(ClientMemError::MmapFailed(uapi::Errno::default().into())); + } + std::slice::from_raw_parts_mut(data as *mut Cell, len) + }; + Ok(Self { + failed: Cell::new(false), + sigbus_impossible, + data, + }) + } + + pub fn access]) -> T>(&self, f: F) -> Result { + unsafe { + if self.sigbus_impossible { + return Ok(f(&mut *self.data)); + } + MEM.with(|m| { + let mref = MemRef { + mem: self, + outer: *m.get(), + }; + *m.get() = &mref; + compiler_fence(Ordering::SeqCst); + let res = f(&mut *self.data); + *m.get() = mref.outer; + compiler_fence(Ordering::SeqCst); + match self.failed.get() { + true => Err(ClientMemError::Sigbus), + _ => Ok(res), + } + }) + } + } + + pub fn len(&self) -> usize { + unsafe { (*self.data).len() } + } +} + +impl Drop for ClientMem { + fn drop(&mut self) { + unsafe { + c::munmap(self.data as _, self.len()); + } + } +} + +struct MemRef { + mem: *const ClientMem, + outer: *const MemRef, +} + +thread_local! { + static MEM: UnsafeCell<*const MemRef> = UnsafeCell::new(ptr::null()); +} + +unsafe fn kill() -> ! { + c::signal(c::SIGBUS, c::SIG_DFL); + raise(c::SIGBUS); + unreachable!(); +} + +unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) { + assert_eq!(sig, c::SIGBUS); + let mut memr_ptr = MEM.with(|m| ptr::read(m.get())); + while !memr_ptr.is_null() { + let memr = &*memr_ptr; + let mem = &*memr.mem; + let lo = mem.data as *mut u8 as usize; + let hi = lo + mem.len(); + let fault_addr = info.si_addr() as usize; + if fault_addr < lo || fault_addr >= hi { + memr_ptr = memr.outer; + continue; + } + let res = c::mmap64( + lo as _, + hi - lo, + c::PROT_WRITE | c::PROT_READ, + c::MAP_ANONYMOUS | c::MAP_PRIVATE, + -1, + 0, + ); + if res == c::MAP_FAILED { + kill(); + } + mem.failed.set(true); + return; + } + kill(); +} + +pub fn init() -> Result<(), ClientMemError> { + unsafe { + let mut action: c::sigaction = MaybeUninit::zeroed().assume_init(); + action.sa_sigaction = + mem::transmute(sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void)); + action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO; + let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut()); + match uapi::map_err!(res) { + Ok(_) => Ok(()), + Err(e) => Err(ClientMemError::SigactionFailed(e.into())), + } + } +} diff --git a/src/event_loop.rs b/src/event_loop.rs new file mode 100644 index 00000000..f7c02549 --- /dev/null +++ b/src/event_loop.rs @@ -0,0 +1,246 @@ +use crate::utils::copyhashmap::CopyHashMap; +use crate::utils::numcell::NumCell; +use std::cell::{Cell, RefCell}; +use std::collections::VecDeque; +use std::rc::{Rc, Weak}; +use thiserror::Error; +use uapi::{c, Errno, OwnedFd}; + +#[derive(Debug, Error)] +pub enum EventLoopError { + #[error("Could not create an epoll fd: {0}")] + CreateFailed(std::io::Error), + #[error("epoll_wait failed: {0}")] + WaitFailed(std::io::Error), + #[error("A dispatcher returned a fatal error: {0}")] + DispatcherError(Box), + #[error("Could not insert an fd to wait on: {0}")] + InsertFailed(std::io::Error), + #[error("Could not modify an fd to wait on: {0}")] + ModifyFailed(std::io::Error), + #[error("Could not remove an fd to wait on: {0}")] + RemoveFailed(std::io::Error), + #[error("Entry is not registered")] + NoEntry, + #[error("Event loop is already destroyed")] + Destroyed, +} + +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct EventLoopId(u64); + +pub trait EventLoopDispatcher { + fn dispatch(&self, events: i32) -> Result<(), Box>; +} + +#[derive(Clone)] +struct Entry { + fd: Option, + dispatcher: Rc, +} + +struct EventLoopData { + epoll: OwnedFd, + run: Cell, + next_id: NumCell, + entries: CopyHashMap, + scheduled: RefCell>, +} + +pub struct EventLoop { + data: Rc, +} + +#[derive(Clone)] +pub struct EventLoopRef { + data: Weak, +} + +impl EventLoopData { + fn new() -> Result { + let epoll = match uapi::epoll_create1(c::EPOLL_CLOEXEC) { + Ok(e) => e, + Err(e) => return Err(EventLoopError::CreateFailed(e.into())), + }; + Ok(Self { + epoll, + run: Cell::new(true), + next_id: NumCell::new(1), + entries: CopyHashMap::new(), + scheduled: RefCell::new(Default::default()), + }) + } + + fn id(&self) -> EventLoopId { + EventLoopId(self.next_id.fetch_add(1)) + } + + fn stop(&self) { + self.run.set(false); + } + + fn insert( + &self, + id: EventLoopId, + fd: Option, + events: i32, + dispatcher: Rc, + ) -> Result<(), EventLoopError> { + let id = id.0; + if let Some(fd) = fd { + let event = c::epoll_event { + events: events as _, + u64: id, + }; + if let Err(e) = uapi::epoll_ctl(self.epoll.raw(), c::EPOLL_CTL_ADD, fd, Some(&event)) { + return Err(EventLoopError::InsertFailed(e.into())); + } + } + self.entries.set(id, Entry { fd, dispatcher }); + Ok(()) + } + + fn modify(&self, id: EventLoopId, events: i32) -> Result<(), EventLoopError> { + let id = id.0; + let entry = match self.entries.get(&id) { + Some(e) => e, + None => return Err(EventLoopError::NoEntry), + }; + if let Some(fd) = entry.fd { + let event = c::epoll_event { + events: events as _, + u64: id, + }; + if let Err(e) = uapi::epoll_ctl(self.epoll.raw(), c::EPOLL_CTL_MOD, fd, Some(&event)) { + return Err(EventLoopError::ModifyFailed(e.into())); + } + } + Ok(()) + } + + fn remove(&self, id: EventLoopId) -> Result<(), EventLoopError> { + let id = id.0; + let entry = match self.entries.remove(&id) { + Some(e) => e, + None => return Err(EventLoopError::NoEntry), + }; + if let Some(fd) = entry.fd { + if let Err(e) = uapi::epoll_ctl(self.epoll.raw(), c::EPOLL_CTL_DEL, fd, None) { + return Err(EventLoopError::RemoveFailed(e.into())); + } + } + Ok(()) + } + + fn schedule(&self, id: EventLoopId) { + self.scheduled.borrow_mut().push_back(id.0); + } + + fn run(&self) -> Result<(), EventLoopError> { + let mut buf = [c::epoll_event { events: 0, u64: 0 }; 16]; + while self.run.get() { + while let Some(id) = self.scheduled.borrow_mut().pop_front() { + if !self.run.get() { + break; + } + if let Some(entry) = self.entries.get(&id) { + if let Err(e) = entry.dispatcher.dispatch(0) { + return Err(EventLoopError::DispatcherError(e)); + } + } + } + let num = match uapi::epoll_wait(self.epoll.raw(), &mut buf, -1) { + Ok(n) => n, + Err(Errno(c::EINTR)) => continue, + Err(e) => return Err(EventLoopError::WaitFailed(e.into())), + }; + for event in &buf[..num] { + if !self.run.get() { + break; + } + let id = event.u64; + let entry = match self.entries.get(&id) { + Some(d) => d, + None => { + log::warn!( + "Client {} created an event but has already been removed", + id, + ); + continue; + } + }; + if let Err(e) = entry.dispatcher.dispatch(event.events as i32) { + return Err(EventLoopError::DispatcherError(e)); + } + } + } + Ok(()) + } +} + +impl EventLoop { + pub fn new() -> Result { + Ok(Self { + data: Rc::new(EventLoopData::new()?), + }) + } + + pub fn to_ref(&self) -> EventLoopRef { + EventLoopRef { + data: Rc::downgrade(&self.data), + } + } + + pub fn run(&self) -> Result<(), EventLoopError> { + self.data.run() + } +} + +impl EventLoopRef { + pub fn id(&self) -> Result { + match self.data.upgrade() { + Some(d) => Ok(d.id()), + None => Err(EventLoopError::Destroyed), + } + } + + pub fn stop(&self) { + if let Some(d) = self.data.upgrade() { + d.stop(); + } + } + + pub fn insert( + &self, + id: EventLoopId, + fd: Option, + events: i32, + dispatcher: Rc, + ) -> Result<(), EventLoopError> { + match self.data.upgrade() { + Some(d) => d.insert(id, fd, events, dispatcher), + None => Err(EventLoopError::Destroyed), + } + } + + pub fn modify(&self, id: EventLoopId, events: i32) -> Result<(), EventLoopError> { + match self.data.upgrade() { + Some(d) => d.modify(id, events), + None => Err(EventLoopError::Destroyed), + } + } + + pub fn remove(&self, id: EventLoopId) -> Result<(), EventLoopError> { + match self.data.upgrade() { + Some(d) => d.remove(id), + None => Err(EventLoopError::Destroyed), + } + } + + pub fn schedule(&self, id: EventLoopId) -> Result<(), EventLoopError> { + match self.data.upgrade() { + Some(d) => Ok(d.schedule(id)), + None => Err(EventLoopError::Destroyed), + } + } +} diff --git a/src/globals.rs b/src/globals.rs new file mode 100644 index 00000000..b4b525cd --- /dev/null +++ b/src/globals.rs @@ -0,0 +1,164 @@ +use crate::ifs::wl_compositor::WlCompositorError; +use crate::ifs::wl_registry::WlRegistry; +use crate::ifs::wl_shm::WlShmError; +use crate::ifs::wl_subcompositor::WlSubcompositorError; +use crate::ifs::xdg_wm_base::XdgWmBaseError; +use crate::objects::{Interface, ObjectId}; +use crate::utils::copyhashmap::CopyHashMap; +use crate::wl_client::{DynEventFormatter, WlClientData, WlClientError}; +use crate::{NumCell, State}; +use ahash::AHashSet; +use std::fmt::{Display, Formatter}; +use std::future::Future; +use std::pin::Pin; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum GlobalError { + #[error("The requested global {0} does not exist")] + GlobalDoesNotExist(GlobalName), + #[error("An error occurred while trying to send all globals via a new registry")] + SendAllError(#[source] Box), + #[error("An error occurred in a wl_compositor")] + WlCompositorError(#[source] Box), + #[error("An error occurred in a wl_shm")] + WlShmError(#[source] Box), + #[error("An error occurred in a wl_subcompositor")] + WlSubcompositorError(#[source] Box), + #[error("An error occurred in a xdg_wm_base")] + XdgWmBaseError(#[source] Box), +} + +efrom!(GlobalError, WlCompositorError, WlCompositorError); +efrom!(GlobalError, WlShmError, WlShmError); +efrom!(GlobalError, WlSubcompositorError, WlSubcompositorError); +efrom!(GlobalError, XdgWmBaseError, XdgWmBaseError); + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct GlobalName(u32); + +impl GlobalName { + pub fn from_raw(id: u32) -> Self { + Self(id) + } + + pub fn raw(self) -> u32 { + self.0 + } +} + +impl Display for GlobalName { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +pub trait GlobalBind { + fn bind<'a>( + self: Rc, + client: &'a Rc, + id: ObjectId, + version: u32, + ) -> Pin> + 'a>>; +} + +pub trait Global: GlobalBind { + fn name(&self) -> GlobalName; + fn interface(&self) -> Interface; + fn version(&self) -> u32; + fn pre_remove(&self); +} + +pub struct Globals { + next_name: NumCell, + registry: CopyHashMap>, +} + +impl Globals { + pub fn new() -> Self { + Self { + next_name: NumCell::new(1), + registry: CopyHashMap::new(), + } + } + + pub fn name(&self) -> GlobalName { + let id = self.next_name.fetch_add(1); + if id == 0 { + panic!("Global names overflowed"); + } + GlobalName(id) + } + + pub fn insert_no_broadcast<'a>(&'a self, global: Rc) { + self.insert_no_broadcast_(&global); + } + + fn insert_no_broadcast_<'a>(&'a self, global: &Rc) { + self.registry.set(global.name(), global.clone()); + } + + pub async fn insert<'a>(&'a self, state: &'a State, global: Rc) { + self.insert_no_broadcast_(&global); + self.broadcast(state, |r| r.global(&global)).await; + } + + pub fn get(&self, name: GlobalName) -> Result, GlobalError> { + self.take(name, false) + } + + pub async fn remove( + &self, + state: &State, + name: GlobalName, + ) -> Result, GlobalError> { + let global = self.take(name, true)?; + global.pre_remove(); + self.broadcast(state, |r| r.global_remove(name)).await; + Ok(global) + } + + pub async fn notify_all( + &self, + client: &WlClientData, + registry: &Rc, + ) -> Result<(), GlobalError> { + let globals = self.registry.lock(); + for global in globals.values() { + if let Err(e) = client.event(registry.global(global)).await { + return Err(GlobalError::SendAllError(Box::new(e))); + } + } + Ok(()) + } + + async fn broadcast) -> DynEventFormatter>(&self, state: &State, f: F) { + let mut clients_to_check = AHashSet::new(); + state.clients.broadcast(|c| { + let registries = c.objects.registries(); + for registry in registries.values() { + if c.event_locked(f(registry)) { + clients_to_check.insert(c.id); + } + } + }); + for client in clients_to_check.drain() { + if let Ok(c) = state.clients.get(client) { + let _ = c.check_queue_size().await; + } + } + } + + fn take(&self, name: GlobalName, remove: bool) -> Result, GlobalError> { + let res = if remove { + self.registry.remove(&name) + } else { + self.registry.get(&name) + }; + match res { + Some(g) => Ok(g), + None => Err(GlobalError::GlobalDoesNotExist(name)), + } + } +} diff --git a/src/ifs/mod.rs b/src/ifs/mod.rs new file mode 100644 index 00000000..b9e074be --- /dev/null +++ b/src/ifs/mod.rs @@ -0,0 +1,10 @@ +pub mod wl_callback; +pub mod wl_compositor; +pub mod wl_display; +pub mod wl_region; +pub mod wl_registry; +pub mod wl_shm; +pub mod wl_shm_pool; +pub mod wl_subcompositor; +pub mod wl_surface; +pub mod xdg_wm_base; diff --git a/src/ifs/wl_callback/mod.rs b/src/ifs/wl_callback/mod.rs new file mode 100644 index 00000000..190e3081 --- /dev/null +++ b/src/ifs/wl_callback/mod.rs @@ -0,0 +1,47 @@ +mod types; + +use crate::objects::{Interface, Object, ObjectError, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::DynEventFormatter; +use std::rc::Rc; +use types::*; + +const DONE: u32 = 0; + +pub struct WlCallback { + id: ObjectId, +} + +impl WlCallback { + pub fn new(id: ObjectId) -> Self { + Self { id } + } + + pub fn done(self: &Rc) -> DynEventFormatter { + Box::new(Done { obj: self.clone() }) + } + + async fn handle_request_( + &self, + _request: u32, + _parser: WlParser<'_, '_>, + ) -> Result<(), ObjectError> { + unreachable!(); + } +} + +handle_request!(WlCallback); + +impl Object for WlCallback { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlCallback + } + + fn num_requests(&self) -> u32 { + 0 + } +} diff --git a/src/ifs/wl_callback/types.rs b/src/ifs/wl_callback/types.rs new file mode 100644 index 00000000..d8dc99a1 --- /dev/null +++ b/src/ifs/wl_callback/types.rs @@ -0,0 +1,23 @@ +use crate::ifs::wl_callback::{WlCallback, DONE}; +use crate::objects::Object; +use crate::utils::buffd::WlFormatter; +use crate::wl_client::EventFormatter; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; + +pub(super) struct Done { + pub obj: Rc, +} +impl EventFormatter for Done { + fn format(self: Box, fmt: &mut WlFormatter<'_>) { + fmt.header(self.obj.id, DONE).uint(0); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Done { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "done(callback_data: 0)") + } +} diff --git a/src/ifs/wl_compositor/mod.rs b/src/ifs/wl_compositor/mod.rs new file mode 100644 index 00000000..29a150e2 --- /dev/null +++ b/src/ifs/wl_compositor/mod.rs @@ -0,0 +1,111 @@ +mod types; + +use crate::globals::{Global, GlobalName}; +use crate::ifs::wl_surface::WlSurface; +use crate::objects::{Interface, Object, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::WlClientData; +use std::rc::Rc; +pub use types::*; +use crate::ifs::wl_region::WlRegion; + +const CREATE_SURFACE: u32 = 0; +const CREATE_REGION: u32 = 1; + +pub struct WlCompositorGlobal { + name: GlobalName, +} + +pub struct WlCompositorObj { + global: Rc, + id: ObjectId, + client: Rc, + version: u32, +} + +impl WlCompositorGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + async fn bind_( + self: Rc, + id: ObjectId, + client: &Rc, + version: u32, + ) -> Result<(), WlCompositorError> { + let obj = Rc::new(WlCompositorObj { + global: self, + id, + client: client.clone(), + version, + }); + client.attach_client_object(obj)?; + Ok(()) + } +} + +impl WlCompositorObj { + async fn create_surface(&self, parser: WlParser<'_, '_>) -> Result<(), CreateSurfaceError> { + let surface: CreateSurface = self.client.parse(self, parser)?; + let surface = Rc::new(WlSurface::new(surface.id, &self.client)); + self.client.attach_client_object(surface)?; + Ok(()) + } + + async fn create_region(&self, parser: WlParser<'_, '_>) -> Result<(), CreateRegionError> { + let region: CreateRegion = self.client.parse(self, parser)?; + let region = Rc::new(WlRegion::new(region.id, &self.client)); + self.client.attach_client_object(region)?; + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlCompositorError> { + match request { + CREATE_SURFACE => self.create_surface(parser).await?, + CREATE_REGION => self.create_region(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +bind!(WlCompositorGlobal); + +impl Global for WlCompositorGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn interface(&self) -> Interface { + Interface::WlCompositor + } + + fn version(&self) -> u32 { + 4 + } + + fn pre_remove(&self) { + unreachable!() + } +} + +handle_request!(WlCompositorObj); + +impl Object for WlCompositorObj { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlCompositor + } + + fn num_requests(&self) -> u32 { + CREATE_REGION + 1 + } +} diff --git a/src/ifs/wl_compositor/types.rs b/src/ifs/wl_compositor/types.rs new file mode 100644 index 00000000..4ce32cfd --- /dev/null +++ b/src/ifs/wl_compositor/types.rs @@ -0,0 +1,76 @@ +use crate::objects::{ObjectError, ObjectId}; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::wl_client::{RequestParser, WlClientError}; +use std::fmt::{Debug, Formatter}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlCompositorError { + #[error(transparent)] + ObjectError(Box), + #[error(transparent)] + ClientError(Box), + #[error("Could not process `create_surface` request")] + CreateSurfaceError(#[source] Box), + #[error("Could not process `create_region` request")] + CreateRegionError(#[source] Box), +} + +efrom!(WlCompositorError, ObjectError, ObjectError); +efrom!(WlCompositorError, ClientError, WlClientError); +efrom!(WlCompositorError, CreateSurfaceError, CreateSurfaceError); +efrom!(WlCompositorError, CreateRegionError, CreateRegionError); + +#[derive(Debug, Error)] +pub enum CreateSurfaceError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} + +efrom!(CreateSurfaceError, ParseFailed, WlParserError); +efrom!(CreateSurfaceError, ClientError, WlClientError); + +#[derive(Debug, Error)] +pub enum CreateRegionError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} + +efrom!(CreateRegionError, ParseFailed, WlParserError); +efrom!(CreateRegionError, ClientError, WlClientError); + +pub(super) struct CreateSurface { + pub id: ObjectId, +} +impl RequestParser<'_> for CreateSurface { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + id: parser.object()?, + }) + } +} +impl Debug for CreateSurface { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "create_surface(id: {})", self.id) + } +} + +pub(super) struct CreateRegion { + pub id: ObjectId, +} +impl RequestParser<'_> for CreateRegion { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + id: parser.object()?, + }) + } +} +impl Debug for CreateRegion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "create_region(id: {})", self.id) + } +} diff --git a/src/ifs/wl_display/mod.rs b/src/ifs/wl_display/mod.rs new file mode 100644 index 00000000..e26f9456 --- /dev/null +++ b/src/ifs/wl_display/mod.rs @@ -0,0 +1,130 @@ +mod types; + +use crate::ifs::wl_callback::WlCallback; +use crate::ifs::wl_registry::WlRegistry; +use crate::objects::{Interface, Object, ObjectError, ObjectId, WL_DISPLAY_ID}; +use crate::utils::buffd::WlParser; +use crate::wl_client::{DynEventFormatter, WlClientData}; +use std::rc::Rc; +pub use types::*; + +const SYNC: u32 = 0; +const GET_REGISTRY: u32 = 1; + +const ERROR: u32 = 0; +const DELETE_ID: u32 = 1; + +const INVALID_OBJECT: u32 = 0; +const INVALID_METHOD: u32 = 1; +const NO_MEMORY: u32 = 2; +const IMPLEMENTATION: u32 = 3; + +pub struct WlDisplay { + client: Rc, +} + +impl WlDisplay { + pub fn new(client: &Rc) -> Self { + Self { + client: client.clone(), + } + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlDisplayError> { + match request { + SYNC => self.sync(parser).await?, + GET_REGISTRY => self.get_registry(parser).await?, + _ => unreachable!(), + } + Ok(()) + } + + async fn sync(&self, parser: WlParser<'_, '_>) -> Result<(), SyncError> { + let sync: Sync = self.client.parse(self, parser)?; + let cb = Rc::new(WlCallback::new(sync.callback)); + self.client.attach_client_object(cb.clone())?; + self.client.event(cb.done()).await?; + self.client + .objects + .remove_obj(&self.client, cb.id()) + .await?; + Ok(()) + } + + async fn get_registry(&self, parser: WlParser<'_, '_>) -> Result<(), GetRegistryError> { + let gr: GetRegistry = self.client.parse(self, parser)?; + let registry = Rc::new(WlRegistry::new(gr.registry, &self.client)); + self.client.attach_client_object(registry.clone())?; + self.client + .state + .globals + .notify_all(&self.client, ®istry) + .await?; + Ok(()) + } + + fn error( + self: &Rc, + object_id: ObjectId, + code: u32, + message: String, + ) -> DynEventFormatter { + Box::new(Error { + obj: self.clone(), + object_id, + code, + message, + }) + } + + pub fn invalid_request(self: &Rc, obj: &dyn Object, request: u32) -> DynEventFormatter { + let id = obj.id(); + let msg = format!( + "Object {} of type {} has no method {}", + id, + obj.interface().name(), + request + ); + self.error(id, INVALID_METHOD, msg) + } + + pub fn invalid_object(self: &Rc, id: ObjectId) -> DynEventFormatter { + let msg = format!("Object {} does not exist", id,); + self.error(id, INVALID_OBJECT, msg) + } + + pub fn implementation_error(self: &Rc, msg: String) -> DynEventFormatter { + self.error(WL_DISPLAY_ID, IMPLEMENTATION, msg) + } + + pub fn delete_id(self: &Rc, id: ObjectId) -> DynEventFormatter { + Box::new(DeleteId { + obj: self.clone(), + id, + }) + } +} + +handle_request!(WlDisplay); + +impl Object for WlDisplay { + fn id(&self) -> ObjectId { + WL_DISPLAY_ID + } + + fn interface(&self) -> Interface { + Interface::WlDisplay + } + + fn num_requests(&self) -> u32 { + GET_REGISTRY + 1 + } + + fn into_display(self: Rc) -> Result, ObjectError> { + Ok(self) + } +} diff --git a/src/ifs/wl_display/types.rs b/src/ifs/wl_display/types.rs new file mode 100644 index 00000000..4216ceaa --- /dev/null +++ b/src/ifs/wl_display/types.rs @@ -0,0 +1,127 @@ +use crate::globals::GlobalError; +use crate::ifs::wl_display::{WlDisplay, DELETE_ID, ERROR}; +use crate::objects::{Object, ObjectError, ObjectId, WL_DISPLAY_ID}; +use crate::utils::buffd::{WlFormatter, WlParser, WlParserError}; +use crate::wl_client::{EventFormatter, RequestParser, WlClientError}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlDisplayError { + #[error("Could not process a get_registry request")] + GetRegistry(#[source] Box), + #[error("A client error occurred")] + SyncError(#[source] Box), +} + +efrom!(WlDisplayError, GetRegistry, GetRegistryError); +efrom!(WlDisplayError, SyncError, SyncError); + +#[derive(Debug, Error)] +pub enum GetRegistryError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error("An object error occurred")] + ObjectError(#[source] Box), + #[error("An object error occurred")] + ClientError(#[source] Box), + #[error("An error occurred while processing globals")] + GlobalError(#[source] Box), +} + +efrom!(GetRegistryError, ParseFailed, WlParserError); +efrom!(GetRegistryError, ObjectError, ObjectError); +efrom!(GetRegistryError, GlobalError, GlobalError); +efrom!(GetRegistryError, ClientError, WlClientError); + +#[derive(Debug, Error)] +pub enum SyncError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error("An object error occurred")] + ObjectError(#[source] Box), + #[error("A client error occurred")] + ClientError(#[source] Box), +} + +efrom!(SyncError, ParseFailed, WlParserError); +efrom!(SyncError, ObjectError, ObjectError); +efrom!(SyncError, ClientError, WlClientError); + +pub(super) struct GetRegistry { + pub registry: ObjectId, +} +impl RequestParser<'_> for GetRegistry { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + registry: parser.object()?, + }) + } +} +impl Debug for GetRegistry { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "get_registry(registry: {})", self.registry) + } +} + +pub(super) struct Sync { + pub callback: ObjectId, +} +impl RequestParser<'_> for Sync { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + callback: parser.object()?, + }) + } +} +impl Debug for Sync { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "sync(callback: {})", self.callback) + } +} + +pub(super) struct DeleteId { + pub obj: Rc, + pub id: ObjectId, +} +impl EventFormatter for DeleteId { + fn format(self: Box, fmt: &mut WlFormatter<'_>) { + fmt.header(WL_DISPLAY_ID, DELETE_ID).object(self.id); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for DeleteId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "delete_id(id: {})", self.id) + } +} + +pub(super) struct Error { + pub obj: Rc, + pub object_id: ObjectId, + pub code: u32, + pub message: String, +} +impl EventFormatter for Error { + fn format(self: Box, fmt: &mut WlFormatter<'_>) { + fmt.header(WL_DISPLAY_ID, ERROR) + .object(self.object_id) + .uint(self.code) + .string(&self.message); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "error(object_id: {}, code: {}, message: {:?})", + self.object_id, self.code, self.message + ) + } +} diff --git a/src/ifs/wl_region/mod.rs b/src/ifs/wl_region/mod.rs new file mode 100644 index 00000000..fb62f5b2 --- /dev/null +++ b/src/ifs/wl_region/mod.rs @@ -0,0 +1,104 @@ +mod types; + +use crate::objects::{Interface, Object, ObjectError, ObjectId}; +use crate::pixman::Region; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::wl_client::{RequestParser, WlClientData}; +use std::cell::RefCell; +use std::rc::Rc; +pub use types::*; +use crate::ifs::wl_display::WlDisplay; + +const DESTROY: u32 = 0; +const ADD: u32 = 1; +const SUBTRACT: u32 = 2; + +pub struct WlRegion { + id: ObjectId, + client: Rc, + rect: RefCell, +} + +impl WlRegion { + pub fn new(id: ObjectId, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + rect: RefCell::new(Region::new()), + } + } + + pub fn region(&self) -> Region { + self.rect.borrow().clone() + } + + async fn destroy(&self, parser: WlParser<'_, '_>) -> Result<(), DestroyError> { + let _destroy: Destroy = self.client.parse(self, parser)?; + self.client.objects.remove_obj(&self.client, self.id).await?; + Ok(()) + } + + async fn add(&self, parser: WlParser<'_, '_>) -> Result<(), AddError> { + let add: Add = self.client.parse(self, parser)?; + if add.width < 0 || add.height < 0 { + return Err(AddError::NegativeExtents); + } + let mut rect = self.rect.borrow_mut(); + *rect = rect.add(&Region::rect(add.x, add.y, add.width as _, add.height as _)); + Ok(()) + } + + async fn subtract(&self, parser: WlParser<'_, '_>) -> Result<(), SubtractError> { + let subtract: Subtract = self.client.parse(self, parser)?; + if subtract.width < 0 || subtract.height < 0 { + return Err(SubtractError::NegativeExtents); + } + let mut rect = self.rect.borrow_mut(); + *rect = rect.subtract(&Region::rect( + subtract.x, + subtract.y, + subtract.width as _, + subtract.height as _, + )); + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlRegionError> { + match request { + DESTROY => self.destroy(parser).await?, + ADD => self.add(parser).await?, + SUBTRACT => self.subtract(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlRegion); + +impl Object for WlRegion { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlRegion + } + + fn num_requests(&self) -> u32 { + SUBTRACT + 1 + } + + fn post_attach(self: Rc) { + self.client.objects.regions.set(self.id, self.clone()); + } + + fn pre_release(&self) -> Result<(), ObjectError> { + self.client.objects.regions.remove(&self.id); + Ok(()) + } +} diff --git a/src/ifs/wl_region/types.rs b/src/ifs/wl_region/types.rs new file mode 100644 index 00000000..0efb18dc --- /dev/null +++ b/src/ifs/wl_region/types.rs @@ -0,0 +1,107 @@ +use crate::objects::{ObjectError, ObjectId}; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::wl_client::RequestParser; +use std::fmt::{Debug, Formatter}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlRegionError { + #[error("Could not process `destroy` request")] + DestroyError(#[from] DestroyError), + #[error("Could not process `add` request")] + AddError(#[from] AddError), + #[error("Could not process `subtract` request")] + SubtractError(#[from] SubtractError), +} + +#[derive(Debug, Error)] +pub enum DestroyError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ObjectError(Box), +} +efrom!(DestroyError, ParseFailed, WlParserError); +efrom!(DestroyError, ObjectError, ObjectError); + +#[derive(Debug, Error)] +pub enum AddError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error("width and/or height are negative")] + NegativeExtents, +} +efrom!(AddError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum SubtractError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error("width and/or height are negative")] + NegativeExtents, +} +efrom!(SubtractError, ParseFailed, WlParserError); + +pub(super) struct Destroy; +impl RequestParser<'_> for Destroy { + fn parse(_parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Destroy { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "destroy()") + } +} + +pub(super) struct Add { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} +impl RequestParser<'_> for Add { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + x: parser.int()?, + y: parser.int()?, + width: parser.int()?, + height: parser.int()?, + }) + } +} +impl Debug for Add { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "add(x: {}, y: {}, width: {}, height: {})", + self.x, self.y, self.width, self.height, + ) + } +} + +pub(super) struct Subtract { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} +impl RequestParser<'_> for Subtract { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + x: parser.int()?, + y: parser.int()?, + width: parser.int()?, + height: parser.int()?, + }) + } +} +impl Debug for Subtract { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "subtract(x: {}, y: {}, width: {}, height: {})", + self.x, self.y, self.width, self.height, + ) + } +} diff --git a/src/ifs/wl_registry/mod.rs b/src/ifs/wl_registry/mod.rs new file mode 100644 index 00000000..113c3baf --- /dev/null +++ b/src/ifs/wl_registry/mod.rs @@ -0,0 +1,91 @@ +mod types; + +use crate::globals::{Global, GlobalName}; +use crate::objects::{Interface, Object, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::{DynEventFormatter, WlClientData}; +use std::rc::Rc; +pub use types::*; + +const BIND: u32 = 0; + +const GLOBAL: u32 = 0; +const GLOBAL_REMOVE: u32 = 1; + +pub struct WlRegistry { + id: ObjectId, + client: Rc, +} + +impl WlRegistry { + pub fn new(id: ObjectId, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + } + } + + pub fn global(self: &Rc, global: &Rc) -> DynEventFormatter { + Box::new(GlobalE { + obj: self.clone(), + global: global.clone(), + }) + } + + pub fn global_remove(self: &Rc, name: GlobalName) -> DynEventFormatter { + Box::new(GlobalRemove { + obj: self.clone(), + name, + }) + } + + async fn bind(&self, parser: WlParser<'_, '_>) -> Result<(), BindError> { + let bind: Bind = self.client.parse(self, parser)?; + let global = self.client.state.globals.get(bind.name)?; + if global.interface().name() != bind.interface { + return Err(BindError::InvalidInterface(InterfaceError { + name: global.name(), + interface: global.interface(), + actual: bind.interface.to_string(), + })); + } + if bind.version > global.version() { + return Err(BindError::InvalidVersion(VersionError { + name: global.name(), + interface: global.interface(), + version: global.version(), + actual: bind.version, + })); + } + global.bind(&self.client, bind.id, bind.version).await?; + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlRegistryError> { + match request { + BIND => self.bind(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlRegistry); + +impl Object for WlRegistry { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlRegistry + } + + fn num_requests(&self) -> u32 { + BIND + 1 + } +} diff --git a/src/ifs/wl_registry/types.rs b/src/ifs/wl_registry/types.rs new file mode 100644 index 00000000..6ef1f55a --- /dev/null +++ b/src/ifs/wl_registry/types.rs @@ -0,0 +1,117 @@ +use crate::globals::{Global, GlobalError, GlobalName}; +use crate::ifs::wl_registry::{WlRegistry, GLOBAL, GLOBAL_REMOVE}; +use crate::objects::{Interface, Object, ObjectId}; +use crate::utils::buffd::{WlFormatter, WlParser, WlParserError}; +use crate::wl_client::{EventFormatter, RequestParser}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlRegistryError { + #[error("Could not process bind request")] + BindError(#[source] Box), +} + +efrom!(WlRegistryError, BindError, BindError); + +#[derive(Debug, Error)] +pub enum BindError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error(transparent)] + GlobalError(Box), + #[error("Tried to bind to global {} of type {} using interface {}", .0.name, .0.interface.name(), .0.actual)] + InvalidInterface(InterfaceError), + #[error("Tried to bind to global {} of type {} and version {} using version {}", .0.name, .0.interface.name(), .0.version, .0.actual)] + InvalidVersion(VersionError), +} + +#[derive(Debug)] +pub struct InterfaceError { + pub name: GlobalName, + pub interface: Interface, + pub actual: String, +} + +#[derive(Debug)] +pub struct VersionError { + pub name: GlobalName, + pub interface: Interface, + pub version: u32, + pub actual: u32, +} + +efrom!(BindError, ParseError, WlParserError); +efrom!(BindError, GlobalError, GlobalError); + +pub(super) struct GlobalE { + pub obj: Rc, + pub global: Rc, +} +impl EventFormatter for GlobalE { + fn format(self: Box, fmt: &mut WlFormatter<'_>) { + fmt.header(self.obj.id, GLOBAL) + .uint(self.global.name().raw()) + .string(self.global.interface().name()) + .uint(self.global.version()); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for GlobalE { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "global(name: {}, interface: {:?}, version: {})", + self.global.name(), + self.global.interface().name(), + self.global.version() + ) + } +} + +pub(super) struct GlobalRemove { + pub obj: Rc, + pub name: GlobalName, +} +impl EventFormatter for GlobalRemove { + fn format(self: Box, fmt: &mut WlFormatter<'_>) { + fmt.header(self.obj.id, GLOBAL_REMOVE).uint(self.name.raw()); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for GlobalRemove { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "global_remove(name: {})", self.name) + } +} + +pub(super) struct Bind<'a> { + pub name: GlobalName, + pub id: ObjectId, + pub interface: &'a str, + pub version: u32, +} +impl<'a> RequestParser<'a> for Bind<'a> { + fn parse(parser: &mut WlParser<'_, 'a>) -> Result { + Ok(Self { + name: parser.global()?, + interface: parser.string()?, + version: parser.uint()?, + id: parser.object()?, + }) + } +} +impl Debug for Bind<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "bind(name: {}, interface: {:?}, version: {}, id: {})", + self.name, self.interface, self.version, self.id + ) + } +} diff --git a/src/ifs/wl_shm/mod.rs b/src/ifs/wl_shm/mod.rs new file mode 100644 index 00000000..59bc321f --- /dev/null +++ b/src/ifs/wl_shm/mod.rs @@ -0,0 +1,136 @@ +mod types; + +use crate::globals::{Global, GlobalName}; +use crate::ifs::wl_shm_pool::WlShmPool; +use crate::objects::{Interface, Object, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::WlClientData; +use std::rc::Rc; +pub use types::*; + +const CREATE_POOL: u32 = 0; + +const FORMAT: u32 = 0; + +pub struct WlShmGlobal { + name: GlobalName, +} + +pub struct WlShmObj { + global: Rc, + id: ObjectId, + client: Rc, +} + +impl WlShmGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + async fn bind_( + self: Rc, + id: ObjectId, + client: &Rc, + _version: u32, + ) -> Result<(), WlShmError> { + let obj = Rc::new(WlShmObj { + global: self, + id, + client: client.clone(), + }); + client.attach_client_object(obj.clone())?; + for &format in Format::formats() { + client + .event(Box::new(FormatE { + obj: obj.clone(), + format, + })) + .await?; + } + Ok(()) + } +} + +impl WlShmObj { + async fn create_pool(&self, parser: WlParser<'_, '_>) -> Result<(), CreatePoolError> { + let create: CreatePool = self.client.parse(self, parser)?; + if create.size < 0 { + return Err(CreatePoolError::NegativeSize); + } + let pool = Rc::new(WlShmPool::new( + create.id, + &self.client, + create.fd, + create.size as usize, + )?); + self.client.attach_client_object(pool)?; + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlShmError> { + match request { + CREATE_POOL => self.create_pool(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Format { + Argb8888, + Xrgb8888, +} + +impl Format { + fn uint(self) -> u32 { + match self { + Format::Argb8888 => 0, + Format::Xrgb8888 => 1, + } + } + + fn formats() -> &'static [Format] { + &[Format::Argb8888, Format::Xrgb8888] + } +} + +bind!(WlShmGlobal); + +impl Global for WlShmGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn interface(&self) -> Interface { + Interface::WlShm + } + + fn version(&self) -> u32 { + 1 + } + + fn pre_remove(&self) { + unreachable!() + } +} + +handle_request!(WlShmObj); + +impl Object for WlShmObj { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlShm + } + + fn num_requests(&self) -> u32 { + CREATE_POOL + 1 + } +} diff --git a/src/ifs/wl_shm/types.rs b/src/ifs/wl_shm/types.rs new file mode 100644 index 00000000..2d101438 --- /dev/null +++ b/src/ifs/wl_shm/types.rs @@ -0,0 +1,80 @@ +use crate::ifs::wl_shm::{Format, WlShmObj, FORMAT}; +use crate::ifs::wl_shm_pool::WlShmPoolError; +use crate::objects::{Object, ObjectError, ObjectId}; +use crate::utils::buffd::{WlFormatter, WlParser, WlParserError}; +use crate::wl_client::{EventFormatter, RequestParser, WlClientError}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; +use uapi::OwnedFd; + +#[derive(Debug, Error)] +pub enum WlShmError { + #[error(transparent)] + ObjectError(Box), + #[error(transparent)] + ClientError(Box), + #[error("Could not process a `create_pool` request")] + CreatePoolError(#[from] CreatePoolError), +} +efrom!(WlShmError, ObjectError, ObjectError); +efrom!(WlShmError, ClientError, WlClientError); + +#[derive(Debug, Error)] +pub enum CreatePoolError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error("The passed size is negative")] + NegativeSize, + #[error(transparent)] + WlShmPoolError(Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(CreatePoolError, ParseError, WlParserError); +efrom!(CreatePoolError, WlShmPoolError, WlShmPoolError); +efrom!(CreatePoolError, ClientError, WlClientError); + +pub(super) struct CreatePool { + pub id: ObjectId, + pub fd: OwnedFd, + pub size: i32, +} +impl RequestParser<'_> for CreatePool { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + id: parser.object()?, + fd: parser.fd()?, + size: parser.int()?, + }) + } +} +impl Debug for CreatePool { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "create_pool(id: {}, fd: {}, size: {})", + self.id, + self.fd.raw(), + self.size + ) + } +} + +pub(super) struct FormatE { + pub obj: Rc, + pub format: Format, +} +impl EventFormatter for FormatE { + fn format(self: Box, fmt: &mut WlFormatter<'_>) { + fmt.header(self.obj.id, FORMAT).uint(self.format.uint()); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for FormatE { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "format(format: {:?})", self.format) + } +} diff --git a/src/ifs/wl_shm_pool/mod.rs b/src/ifs/wl_shm_pool/mod.rs new file mode 100644 index 00000000..cbd0875d --- /dev/null +++ b/src/ifs/wl_shm_pool/mod.rs @@ -0,0 +1,94 @@ +mod types; + +use crate::clientmem::ClientMem; +use crate::objects::{Interface, Object, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::WlClientData; +use std::cell::RefCell; +use std::rc::Rc; +pub use types::*; +use uapi::OwnedFd; + +const CREATE_BUFFER: u32 = 0; +const DESTROY: u32 = 1; +const RESIZE: u32 = 2; + +pub struct WlShmPool { + id: ObjectId, + client: Rc, + fd: OwnedFd, + mem: RefCell>, +} + +impl WlShmPool { + pub fn new( + id: ObjectId, + client: &Rc, + fd: OwnedFd, + len: usize, + ) -> Result { + Ok(Self { + id, + client: client.clone(), + mem: RefCell::new(Rc::new(ClientMem::new(fd.raw(), len)?)), + fd, + }) + } + + async fn create_buffer(&self, parser: WlParser<'_, '_>) -> Result<(), CreateBufferError> { + let create: CreateBuffer = self.client.parse(self, parser)?; + Ok(()) + } + + async fn destroy(&self, parser: WlParser<'_, '_>) -> Result<(), DestroyError> { + let _destroy: Destroy = self.client.parse(self, parser)?; + self.client + .objects + .remove_obj(&self.client, self.id) + .await?; + Ok(()) + } + + async fn resize(&self, parser: WlParser<'_, '_>) -> Result<(), ResizeError> { + let resize: Resize = self.client.parse(self, parser)?; + let mut mem = self.mem.borrow_mut(); + if resize.size < 0 { + return Err(ResizeError::NegativeSize); + } + if (resize.size as usize) < mem.len() { + return Err(ResizeError::CannotShrink); + } + *mem = Rc::new(ClientMem::new(self.fd.raw(), resize.size as usize)?); + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlShmPoolError> { + match request { + CREATE_BUFFER => self.create_buffer(parser).await?, + DESTROY => self.destroy(parser).await?, + RESIZE => self.resize(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlShmPool); + +impl Object for WlShmPool { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlShmPool + } + + fn num_requests(&self) -> u32 { + RESIZE + 1 + } +} diff --git a/src/ifs/wl_shm_pool/types.rs b/src/ifs/wl_shm_pool/types.rs new file mode 100644 index 00000000..2726958d --- /dev/null +++ b/src/ifs/wl_shm_pool/types.rs @@ -0,0 +1,117 @@ +use crate::clientmem::ClientMemError; +use crate::objects::{ObjectError, ObjectId}; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::wl_client::{RequestParser, WlClientError}; +use std::fmt::{Debug, Formatter}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlShmPoolError { + #[error(transparent)] + ObjectError(Box), + #[error(transparent)] + ClientError(Box), + #[error("Could not process a `create_buffer` request")] + CreateBufferError(#[from] CreateBufferError), + #[error("Could not process a `destroy` request")] + DestroyError(#[from] DestroyError), + #[error("Could not process a `resize` request")] + ResizeError(#[from] ResizeError), + #[error(transparent)] + ClientMemError(Box), +} +efrom!(WlShmPoolError, ObjectError, ObjectError); +efrom!(WlShmPoolError, ClientError, WlClientError); +efrom!(WlShmPoolError, ClientMemError, ClientMemError); + +#[derive(Debug, Error)] +pub enum CreateBufferError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error(transparent)] + ObjectError(Box), +} +efrom!(CreateBufferError, ParseError, WlParserError); +efrom!(CreateBufferError, ObjectError, ObjectError); + +#[derive(Debug, Error)] +pub enum DestroyError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error(transparent)] + ObjectError(Box), +} +efrom!(DestroyError, ParseError, WlParserError); +efrom!(DestroyError, ObjectError, ObjectError); + +#[derive(Debug, Error)] +pub enum ResizeError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error("Tried to shrink the pool")] + CannotShrink, + #[error("Requested size is negative")] + NegativeSize, + #[error(transparent)] + ClientMemError(Box), +} +efrom!(ResizeError, ParseError, WlParserError); +efrom!(ResizeError, ClientMemError, ClientMemError); + +pub(super) struct CreateBuffer { + pub id: ObjectId, + pub offset: i32, + pub width: i32, + pub height: i32, + pub stride: i32, + pub format: u32, +} +impl RequestParser<'_> for CreateBuffer { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + id: parser.object()?, + offset: parser.int()?, + width: parser.int()?, + height: parser.int()?, + stride: parser.int()?, + format: parser.uint()?, + }) + } +} +impl Debug for CreateBuffer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "create_buffer(id: {}, offset: {}, width: {}, height: {}, stride: {}, format: {})", + self.id, self.offset, self.width, self.height, self.stride, self.format, + ) + } +} + +pub(super) struct Destroy; +impl RequestParser<'_> for Destroy { + fn parse(_parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Destroy { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "destroy()",) + } +} + +pub(super) struct Resize { + pub size: i32, +} +impl RequestParser<'_> for Resize { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + size: parser.int()?, + }) + } +} +impl Debug for Resize { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "resize(size: {})", self.size,) + } +} diff --git a/src/ifs/wl_subcompositor/mod.rs b/src/ifs/wl_subcompositor/mod.rs new file mode 100644 index 00000000..960cc4e4 --- /dev/null +++ b/src/ifs/wl_subcompositor/mod.rs @@ -0,0 +1,80 @@ +mod types; + +use crate::globals::{Global, GlobalName}; +use crate::objects::{Interface, Object, ObjectError, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::WlClientData; +use std::rc::Rc; +pub use types::*; + +pub struct WlSubcompositorGlobal { + name: GlobalName, +} + +pub struct WlSubcompositorObj { + global: Rc, + id: ObjectId, +} + +impl WlSubcompositorGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + async fn bind_( + self: Rc, + id: ObjectId, + client: &WlClientData, + _version: u32, + ) -> Result<(), WlSubcompositorError> { + let obj = Rc::new(WlSubcompositorObj { global: self, id }); + client.attach_client_object(obj)?; + Ok(()) + } +} + +impl WlSubcompositorObj { + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), ObjectError> { + unreachable!(); + } +} + +bind!(WlSubcompositorGlobal); + +impl Global for WlSubcompositorGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn interface(&self) -> Interface { + Interface::WlSubcompositor + } + + fn version(&self) -> u32 { + 1 + } + + fn pre_remove(&self) { + unreachable!() + } +} + +handle_request!(WlSubcompositorObj); + +impl Object for WlSubcompositorObj { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlSubcompositor + } + + fn num_requests(&self) -> u32 { + 0 + } +} diff --git a/src/ifs/wl_subcompositor/types.rs b/src/ifs/wl_subcompositor/types.rs new file mode 100644 index 00000000..90b4c17e --- /dev/null +++ b/src/ifs/wl_subcompositor/types.rs @@ -0,0 +1,14 @@ +use crate::objects::ObjectError; +use crate::wl_client::WlClientError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlSubcompositorError { + #[error(transparent)] + ObjectError(Box), + #[error(transparent)] + ClientError(Box), +} + +efrom!(WlSubcompositorError, ObjectError, ObjectError); +efrom!(WlSubcompositorError, ClientError, WlClientError); diff --git a/src/ifs/wl_surface/mod.rs b/src/ifs/wl_surface/mod.rs new file mode 100644 index 00000000..d461e7d3 --- /dev/null +++ b/src/ifs/wl_surface/mod.rs @@ -0,0 +1,162 @@ +mod types; + +use std::cell::Cell; +use crate::objects::{Interface, Object, ObjectError, ObjectId}; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::wl_client::{RequestParser, WlClientData}; +use std::rc::Rc; +pub use types::*; +use crate::pixman::Region; + +const DESTROY: u32 = 0; +const ATTACH: u32 = 1; +const DAMAGE: u32 = 2; +const FRAME: u32 = 3; +const SET_OPAQUE_REGION: u32 = 4; +const SET_INPUT_REGION: u32 = 5; +const COMMIT: u32 = 6; +const SET_BUFFER_TRANSFORM: u32 = 7; +const SET_BUFFER_SCALE: u32 = 8; +const DAMAGE_BUFFER: u32 = 9; + +const ENTER: u32 = 0; +const LEAVE: u32 = 1; + +const INVALID_SCALE: u32 = 0; +const INVALID_TRANSFORM: u32 = 1; +const INVALID_SIZE: u32 = 2; + +pub struct WlSurface { + id: ObjectId, + client: Rc, + pending: PendingState, +} + +#[derive(Default)] +struct PendingState { + opaque_region: Cell>, + input_region: Cell>, +} + +impl WlSurface { + pub fn new(id: ObjectId, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + pending: Default::default(), + } + } + + fn parse<'a, T: RequestParser<'a>>( + &self, + parser: WlParser<'_, 'a>, + ) -> Result { + self.client.parse(self, parser) + } + + async fn destroy(&self, parser: WlParser<'_, '_>) -> Result<(), DestroyError> { + let destroy: Destroy = self.parse(parser)?; + Ok(()) + } + + async fn attach(&self, parser: WlParser<'_, '_>) -> Result<(), AttachError> { + let attach: Attach = self.parse(parser)?; + Ok(()) + } + + async fn damage(&self, parser: WlParser<'_, '_>) -> Result<(), DamageError> { + let damage: Damage = self.parse(parser)?; + Ok(()) + } + + async fn frame(&self, parser: WlParser<'_, '_>) -> Result<(), FrameError> { + let frame: Frame = self.parse(parser)?; + Ok(()) + } + + async fn set_opaque_region( + &self, + parser: WlParser<'_, '_>, + ) -> Result<(), SetOpaqueRegionError> { + let region: SetOpaqueRegion = self.parse(parser)?; + let region = self.client.get_region(region.region)?; + self.pending.opaque_region.set(Some(region.region())); + Ok(()) + } + + async fn set_input_region(&self, parser: WlParser<'_, '_>) -> Result<(), SetInputRegionError> { + let region: SetInputRegion = self.parse(parser)?; + let region = self.client.get_region(region.region)?; + self.pending.input_region.set(Some(region.region())); + Ok(()) + } + + async fn commit(&self, parser: WlParser<'_, '_>) -> Result<(), CommitError> { + let commit: Commit = self.parse(parser)?; + Ok(()) + } + + async fn set_buffer_transform( + &self, + parser: WlParser<'_, '_>, + ) -> Result<(), SetBufferTransformError> { + let transform: SetBufferTransform = self.parse(parser)?; + Ok(()) + } + + async fn set_buffer_scale(&self, parser: WlParser<'_, '_>) -> Result<(), SetBufferScaleError> { + let scale: SetBufferScale = self.parse(parser)?; + Ok(()) + } + + async fn damage_buffer(&self, parser: WlParser<'_, '_>) -> Result<(), DamageBufferError> { + let damage: DamageBuffer = self.parse(parser)?; + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), WlSurfaceError> { + match request { + DESTROY => self.destroy(parser).await?, + ATTACH => self.attach(parser).await?, + DAMAGE => self.damage(parser).await?, + FRAME => self.frame(parser).await?, + SET_OPAQUE_REGION => self.set_opaque_region(parser).await?, + SET_INPUT_REGION => self.set_input_region(parser).await?, + COMMIT => self.commit(parser).await?, + SET_BUFFER_TRANSFORM => self.set_buffer_transform(parser).await?, + SET_BUFFER_SCALE => self.set_buffer_scale(parser).await?, + DAMAGE_BUFFER => self.damage_buffer(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlSurface); + +impl Object for WlSurface { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::WlSurface + } + + fn num_requests(&self) -> u32 { + DAMAGE_BUFFER + 1 + } + + fn pre_release(&self) -> Result<(), ObjectError> { + self.client.objects.surfaces.remove(&self.id); + Ok(()) + } + + fn post_attach(self: Rc) { + self.client.objects.surfaces.set(self.id, self.clone()); + } +} diff --git a/src/ifs/wl_surface/types.rs b/src/ifs/wl_surface/types.rs new file mode 100644 index 00000000..10fb50bb --- /dev/null +++ b/src/ifs/wl_surface/types.rs @@ -0,0 +1,299 @@ +use crate::objects::ObjectId; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::wl_client::{RequestParser, WlClientError}; +use std::fmt::{Debug, Formatter}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlSurfaceError { + #[error("Could not process `destroy` request")] + DestroyError(#[source] Box), + #[error("Could not process `attach` request")] + AttachError(#[source] Box), + #[error("Could not process `damage` request")] + DamageError(#[source] Box), + #[error("Could not process `frame` request")] + FrameError(#[source] Box), + #[error("Could not process `set_opaque_region` request")] + SetOpaqueRegionError(#[source] Box), + #[error("Could not process `set_input_region` request")] + SetInputRegionError(#[source] Box), + #[error("Could not process `commit` request")] + CommitError(#[source] Box), + #[error("Could not process `set_buffer_transform` request")] + SetBufferTransformError(#[source] Box), + #[error("Could not process `set_buffer_scale_error` request")] + SetBufferScaleError(#[source] Box), + #[error("Could not process `damage_buffer` request")] + DamageBufferError(#[source] Box), +} +efrom!(WlSurfaceError, DestroyError, DestroyError); +efrom!(WlSurfaceError, AttachError, AttachError); +efrom!(WlSurfaceError, DamageError, DamageError); +efrom!(WlSurfaceError, FrameError, FrameError); +efrom!(WlSurfaceError, SetOpaqueRegionError, SetOpaqueRegionError); +efrom!(WlSurfaceError, SetInputRegionError, SetInputRegionError); +efrom!(WlSurfaceError, CommitError, CommitError); +efrom!( + WlSurfaceError, + SetBufferTransformError, + SetBufferTransformError +); +efrom!(WlSurfaceError, SetBufferScaleError, SetBufferScaleError); +efrom!(WlSurfaceError, DamageBufferError, DamageBufferError); + +#[derive(Debug, Error)] +pub enum DestroyError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(DestroyError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum AttachError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(AttachError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum DamageError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(DamageError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum FrameError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(FrameError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum SetOpaqueRegionError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(SetOpaqueRegionError, ParseFailed, WlParserError); +efrom!(SetOpaqueRegionError, ClientError, WlClientError); + +#[derive(Debug, Error)] +pub enum SetInputRegionError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(SetInputRegionError, ParseFailed, WlParserError); +efrom!(SetInputRegionError, ClientError, WlClientError); + +#[derive(Debug, Error)] +pub enum CommitError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(CommitError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum SetBufferTransformError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(SetBufferTransformError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum SetBufferScaleError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(SetBufferScaleError, ParseFailed, WlParserError); + +#[derive(Debug, Error)] +pub enum DamageBufferError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), +} +efrom!(DamageBufferError, ParseFailed, WlParserError); + +pub(super) struct Destroy; +impl RequestParser<'_> for Destroy { + fn parse(_parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Destroy { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "destroy()") + } +} + +pub(super) struct Attach { + pub buffer: ObjectId, + pub x: i32, + pub y: i32, +} +impl RequestParser<'_> for Attach { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + buffer: parser.object()?, + x: parser.int()?, + y: parser.int()?, + }) + } +} +impl Debug for Attach { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "attach(buffer: {}, x: {}, y: {})", + self.buffer, self.x, self.y + ) + } +} + +pub(super) struct Damage { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} +impl RequestParser<'_> for Damage { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + x: parser.int()?, + y: parser.int()?, + width: parser.int()?, + height: parser.int()?, + }) + } +} +impl Debug for Damage { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "damage(x: {}, y: {}, width: {}, height: {})", + self.x, self.y, self.width, self.height + ) + } +} + +pub(super) struct Frame { + pub callback: ObjectId, +} +impl RequestParser<'_> for Frame { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + callback: parser.object()?, + }) + } +} +impl Debug for Frame { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "frame(callback: {})", self.callback) + } +} + +pub(super) struct SetOpaqueRegion { + pub region: ObjectId, +} +impl RequestParser<'_> for SetOpaqueRegion { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + region: parser.object()?, + }) + } +} +impl Debug for SetOpaqueRegion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "set_opaque_region(region: {})", self.region) + } +} + +pub(super) struct SetInputRegion { + pub region: ObjectId, +} +impl RequestParser<'_> for SetInputRegion { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + region: parser.object()?, + }) + } +} +impl Debug for SetInputRegion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "set_input_region(region: {})", self.region) + } +} + +pub(super) struct Commit; +impl RequestParser<'_> for Commit { + fn parse(_parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Commit { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "commit()") + } +} + +pub(super) struct SetBufferTransform { + pub transform: i32, +} +impl RequestParser<'_> for SetBufferTransform { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + transform: parser.int()?, + }) + } +} +impl Debug for SetBufferTransform { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "set_buffer_transform(transform: {})", self.transform) + } +} + +pub(super) struct SetBufferScale { + pub scale: i32, +} +impl RequestParser<'_> for SetBufferScale { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + scale: parser.int()?, + }) + } +} +impl Debug for SetBufferScale { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "set_buffer_scale(scale: {})", self.scale) + } +} + +pub(super) struct DamageBuffer { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} +impl RequestParser<'_> for DamageBuffer { + fn parse(parser: &mut WlParser<'_, '_>) -> Result { + Ok(Self { + x: parser.int()?, + y: parser.int()?, + width: parser.int()?, + height: parser.int()?, + }) + } +} +impl Debug for DamageBuffer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "damage_buffer(x: {}, y: {}, width: {}, height: {})", + self.x, self.y, self.width, self.height + ) + } +} diff --git a/src/ifs/xdg_wm_base/mod.rs b/src/ifs/xdg_wm_base/mod.rs new file mode 100644 index 00000000..64db8476 --- /dev/null +++ b/src/ifs/xdg_wm_base/mod.rs @@ -0,0 +1,85 @@ +mod types; + +use crate::globals::{Global, GlobalName}; +use crate::objects::{Interface, Object, ObjectError, ObjectId}; +use crate::utils::buffd::WlParser; +use crate::wl_client::WlClientData; +use std::rc::Rc; +pub use types::*; + +pub struct XdgWmBaseGlobal { + name: GlobalName, +} + +pub struct XdgWmBaseObj { + global: Rc, + id: ObjectId, + version: u32, +} + +impl XdgWmBaseGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + async fn bind_( + self: Rc, + id: ObjectId, + client: &WlClientData, + version: u32, + ) -> Result<(), XdgWmBaseError> { + let obj = Rc::new(XdgWmBaseObj { + global: self, + id, + version, + }); + client.attach_client_object(obj)?; + Ok(()) + } +} + +impl XdgWmBaseObj { + async fn handle_request_( + &self, + request: u32, + parser: WlParser<'_, '_>, + ) -> Result<(), ObjectError> { + unreachable!(); + } +} + +bind!(XdgWmBaseGlobal); + +impl Global for XdgWmBaseGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn interface(&self) -> Interface { + Interface::XdgWmBase + } + + fn version(&self) -> u32 { + 3 + } + + fn pre_remove(&self) { + unreachable!() + } +} + +handle_request!(XdgWmBaseObj); + +impl Object for XdgWmBaseObj { + fn id(&self) -> ObjectId { + self.id + } + + fn interface(&self) -> Interface { + Interface::XdgWmBase + } + + fn num_requests(&self) -> u32 { + 0 + } +} diff --git a/src/ifs/xdg_wm_base/types.rs b/src/ifs/xdg_wm_base/types.rs new file mode 100644 index 00000000..864a5a3f --- /dev/null +++ b/src/ifs/xdg_wm_base/types.rs @@ -0,0 +1,14 @@ +use crate::objects::ObjectError; +use crate::wl_client::WlClientError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum XdgWmBaseError { + #[error(transparent)] + ObjectError(Box), + #[error(transparent)] + ClientError(Box), +} + +efrom!(XdgWmBaseError, ObjectError, ObjectError); +efrom!(XdgWmBaseError, ClientError, WlClientError); diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 00000000..807441c9 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,48 @@ +macro_rules! efrom { + ($ename:ty, $vname:ident, $sname:ty) => { + impl From<$sname> for $ename { + fn from(e: $sname) -> Self { + Self::$vname(Box::new(e)) + } + } + }; +} + +macro_rules! handle_request { + ($oname:ty) => { + impl crate::objects::ObjectHandleRequest for $oname { + fn handle_request<'a>( + &'a self, + request: u32, + parser: crate::utils::buffd::WlParser<'a, 'a>, + ) -> std::pin::Pin< + Box> + 'a>, + > { + Box::pin(async move { + self.handle_request_(request, parser).await?; + Ok(()) + }) + } + } + }; +} + +macro_rules! bind { + ($oname:ty) => { + impl crate::globals::GlobalBind for $oname { + fn bind<'a>( + self: std::rc::Rc, + client: &'a std::rc::Rc, + id: crate::objects::ObjectId, + version: u32, + ) -> std::pin::Pin< + Box> + 'a>, + > { + Box::pin(async move { + self.bind_(id, client, version).await?; + Ok(()) + }) + } + } + }; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..e08193a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,78 @@ +use crate::acceptor::AcceptorError; +use crate::clientmem::ClientMemError; +use crate::event_loop::EventLoopError; +use crate::globals::Globals; +use crate::ifs::wl_compositor::WlCompositorGlobal; +use crate::ifs::wl_shm::WlShmGlobal; +use crate::ifs::wl_subcompositor::WlSubcompositorGlobal; +use crate::ifs::xdg_wm_base::XdgWmBaseGlobal; +use crate::sighand::SighandError; +use crate::state::State; +use crate::utils::numcell::NumCell; +use crate::wl_client::WlClients; +use anyhow::anyhow; +use log::LevelFilter; +use std::rc::Rc; +use thiserror::Error; + +#[macro_use] +mod macros; +mod acceptor; +mod async_engine; +mod clientmem; +mod event_loop; +mod globals; +mod ifs; +mod objects; +mod pixman; +mod sighand; +mod state; +mod time; +mod utils; +mod wheel; +mod wl_client; + +fn main() { + env_logger::builder() + .filter_level(LevelFilter::Trace) + .init(); + if let Err(e) = main_() { + log::error!("A fatal error occurred: {:#}", anyhow!(e)); + std::process::exit(1); + } +} + +#[derive(Debug, Error)] +enum MainError { + #[error("The client acceptor caused an error")] + AcceptorError(#[from] AcceptorError), + #[error("The event loop caused an error")] + EventLoopError(#[from] EventLoopError), + #[error("The signal handler caused an error")] + SighandError(#[from] SighandError), + #[error("The clientmem subsystem caused an error")] + ClientmemError(#[from] ClientMemError), +} + +fn main_() -> Result<(), MainError> { + clientmem::init()?; + let el = event_loop::EventLoop::new().unwrap(); + sighand::install(&el.to_ref())?; + let wheel = wheel::WheelRef::new(&el.to_ref()).unwrap(); + let engine = Rc::new(async_engine::AsyncEngine::new(&el.to_ref(), &wheel).unwrap()); + let globals = Globals::new(); + globals.insert_no_broadcast(Rc::new(WlCompositorGlobal::new(globals.name()))); + globals.insert_no_broadcast(Rc::new(WlShmGlobal::new(globals.name()))); + globals.insert_no_broadcast(Rc::new(WlSubcompositorGlobal::new(globals.name()))); + globals.insert_no_broadcast(Rc::new(XdgWmBaseGlobal::new(globals.name()))); + let state = Rc::new(State { + eng: engine, + el: el.to_ref(), + clients: WlClients::new(), + next_name: NumCell::new(1), + globals, + }); + acceptor::Acceptor::install(&state)?; + el.run()?; + Ok(()) +} diff --git a/src/objects.rs b/src/objects.rs new file mode 100644 index 00000000..0d35b7d1 --- /dev/null +++ b/src/objects.rs @@ -0,0 +1,249 @@ +use crate::ifs::wl_compositor::WlCompositorError; +use crate::ifs::wl_display::{WlDisplay, WlDisplayError}; +use crate::ifs::wl_registry::{WlRegistry, WlRegistryError}; +use crate::ifs::wl_shm::WlShmError; +use crate::ifs::wl_shm_pool::WlShmPoolError; +use crate::ifs::wl_surface::{WlSurface, WlSurfaceError}; +use crate::utils::buffd::{WlParser, WlParserError}; +use crate::utils::copyhashmap::CopyHashMap; +use crate::wl_client::{WlClientData, WlClientError}; +use ahash::AHashMap; +use std::cell::{RefCell, RefMut}; +use std::fmt::{Display, Formatter}; +use std::future::Future; +use std::mem; +use std::pin::Pin; +use std::rc::Rc; +use thiserror::Error; +use crate::ifs::wl_region::{WlRegion, WlRegionError}; + +#[derive(Debug, Error)] +pub enum ObjectError { + #[error("A client error occurred")] + ClientError(#[source] Box), + #[error("Cannot parse the message")] + ParserError(#[source] Box), + #[error("Server tried to allocate more than 0x1_00_00_00 ids")] + TooManyIds, + #[error("The server object id is out of bounds")] + ServerIdOutOfBounds, + #[error("The object id is unknown")] + UnknownId, + #[error("The id is already in use")] + IdAlreadyInUse, + #[error("The client object id is out of bounds")] + ClientIdOutOfBounds, + #[error("An error occurred in a `wl_display`")] + WlDisplayError(#[source] Box), + #[error("An error occurred in a `wl_registry`")] + WlRegistryError(#[source] Box), + #[error("Could not add object {0} to the client")] + AddObjectError(ObjectId, #[source] Box), + #[error("An error occurred in a `wl_surface`")] + WlSurfaceError(#[source] Box), + #[error("An error occurred in a `wl_compositor`")] + WlCompositorError(#[source] Box), + #[error("An error occurred in a `wl_shm`")] + WlShmError(#[source] Box), + #[error("An error occurred in a `wl_shm_pool`")] + WlShmPoolError(#[source] Box), + #[error("An error occurred in a `wl_region`")] + WlRegionError(#[source] Box), + #[error("Object {0} is not a display")] + NotADisplay(ObjectId), +} + +efrom!(ObjectError, ClientError, WlClientError); +efrom!(ObjectError, ParserError, WlParserError); +efrom!(ObjectError, WlDisplayError, WlDisplayError); +efrom!(ObjectError, WlRegistryError, WlRegistryError); +efrom!(ObjectError, WlSurfaceError, WlSurfaceError); +efrom!(ObjectError, WlCompositorError, WlCompositorError); +efrom!(ObjectError, WlShmError, WlShmError); +efrom!(ObjectError, WlShmPoolError, WlShmPoolError); +efrom!(ObjectError, WlRegionError, WlRegionError); + +pub const WL_DISPLAY_ID: ObjectId = ObjectId(1); + +#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub struct ObjectId(u32); + +impl ObjectId { + pub fn from_raw(raw: u32) -> Self { + Self(raw) + } + + pub fn raw(self) -> u32 { + self.0 + } +} + +impl Display for ObjectId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +pub trait ObjectHandleRequest { + fn handle_request<'a>( + &'a self, + request: u32, + parser: WlParser<'a, 'a>, + ) -> Pin> + 'a>>; +} + +pub trait Object: ObjectHandleRequest { + fn id(&self) -> ObjectId; + fn interface(&self) -> Interface; + fn num_requests(&self) -> u32; + fn pre_release(&self) -> Result<(), ObjectError> { + Ok(()) + } + fn post_attach(self: Rc) {} + fn into_display(self: Rc) -> Result, ObjectError> { + Err(ObjectError::NotADisplay(self.id())) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Interface { + WlDisplay, + WlCallback, + WlCompositor, + WlRegistry, + WlShm, + WlShmPool, + WlSubcompositor, + XdgWmBase, + WlSurface, + WlRegion, +} + +impl Interface { + pub fn name(self) -> &'static str { + match self { + Interface::WlDisplay => "wl_display", + Interface::WlCallback => "wl_callback", + Interface::WlCompositor => "wl_compositor", + Interface::WlRegistry => "wl_registry", + Interface::WlShm => "wl_shm", + Interface::WlSubcompositor => "wl_subcompositor", + Interface::XdgWmBase => "xdg_wm_base", + Interface::WlSurface => "wl_surface", + Interface::WlShmPool => "wl_shm_pool", + Interface::WlRegion => "wl_region", + } + } +} + +pub struct Objects { + registry: CopyHashMap>, + registries: CopyHashMap>, + pub surfaces: CopyHashMap>, + pub regions: CopyHashMap>, + ids: RefCell>, +} + +const MIN_SERVER_ID: u32 = 0xff000000; +const SEG_SIZE: usize = 8 * mem::size_of::(); + +impl Objects { + pub fn new() -> Self { + Self { + registry: Default::default(), + registries: Default::default(), + surfaces: Default::default(), + regions: Default::default(), + ids: RefCell::new(vec![]), + } + } + + pub fn destroy(&self) { + self.registry.clear(); + self.registries.clear(); + self.surfaces.clear(); + } + + fn id(&self, client_data: &WlClientData) -> Result { + const MAX_ID_OFFSET: u32 = u32::MAX - MIN_SERVER_ID; + let offset = self.id_offset(); + if offset > MAX_ID_OFFSET { + log::error!( + "Client {} caused the server to allocate more than 0x{:x} ids", + client_data.id, + MAX_ID_OFFSET + 1 + ); + return Err(ObjectError::TooManyIds); + } + Ok(ObjectId(MIN_SERVER_ID + offset)) + } + + pub fn get_obj(&self, id: ObjectId) -> Result, ObjectError> { + match self.registry.get(&id) { + Some(o) => Ok(o), + _ => Err(ObjectError::UnknownId), + } + } + + pub fn add_client_object(&self, obj: Rc) -> Result<(), ObjectError> { + let id = obj.id(); + let res = (|| { + if id.0 == 0 || id.0 >= MIN_SERVER_ID { + return Err(ObjectError::ClientIdOutOfBounds); + } + if self.registry.contains(&id) { + return Err(ObjectError::IdAlreadyInUse); + } + self.registry.set(id, obj.clone()); + obj.post_attach(); + Ok(()) + })(); + if let Err(e) = res { + return Err(ObjectError::AddObjectError(id, Box::new(e))); + } + Ok(()) + } + + pub async fn remove_obj( + &self, + client_data: &WlClientData, + id: ObjectId, + ) -> Result<(), ObjectError> { + let obj = match self.registry.remove(&id) { + Some(o) => o, + _ => return Err(ObjectError::UnknownId), + }; + obj.pre_release()?; + if id.0 >= MIN_SERVER_ID { + let offset = (id.0 - MIN_SERVER_ID) as usize; + let pos = offset / SEG_SIZE; + let seg_offset = offset % SEG_SIZE; + let mut ids = self.ids.borrow_mut(); + if ids.len() <= pos { + return Err(ObjectError::ServerIdOutOfBounds); + } + ids[pos] |= 1 << seg_offset; + } + client_data + .event(client_data.display()?.delete_id(id)) + .await?; + Ok(()) + } + + pub fn registries(&self) -> RefMut>> { + self.registries.lock() + } + + fn id_offset(&self) -> u32 { + let mut ids = self.ids.borrow_mut(); + for (pos, seg) in ids.iter_mut().enumerate() { + if *seg != 0 { + let offset = seg.trailing_zeros(); + *seg &= !(1 << offset); + return (pos * SEG_SIZE) as u32 + offset; + } + } + ids.push(!1); + ((ids.len() - 1) * SEG_SIZE) as u32 + } +} diff --git a/src/pixman/mod.rs b/src/pixman/mod.rs new file mode 100644 index 00000000..fc4f674a --- /dev/null +++ b/src/pixman/mod.rs @@ -0,0 +1,96 @@ +use std::ops::{Add, Sub}; +use std::ptr; +use uapi::c; + +#[link(name = "pixman-1")] +extern "C" { + fn pixman_region32_init(region: *mut Region); + fn pixman_region32_init_rect( + region: *mut Region, + x: c::c_int, + y: c::c_int, + width: c::c_uint, + height: c::c_uint, + ); + fn pixman_region32_fini(region: *mut Region); + fn pixman_region32_copy(dst: *mut Region, src: *const Region); + fn pixman_region32_union(dst: *mut Region, a: *const Region, b: *const Region); + fn pixman_region32_subtract(dst: *mut Region, a: *const Region, b: *const Region); +} + +#[repr(C)] +#[derive(Copy, Clone, Default, Debug)] +pub struct Box32 { + x1: i32, + y1: i32, + x2: i32, + y2: i32, +} + +#[repr(C)] +struct RegionData { + size: c::c_long, + num_rects: c::c_long, + // rects: [Box32; size], +} + +#[repr(C)] +pub struct Region { + extents: Box32, + data: *mut RegionData, +} + +impl Region { + pub fn new() -> Self { + let mut slf = Region { + extents: Default::default(), + data: ptr::null_mut(), + }; + unsafe { + pixman_region32_init(&mut slf); + } + slf + } + + pub fn rect(x: i32, y: i32, width: u32, height: u32) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_init_rect(&mut new, x as _, y as _, width as _, height as _); + } + new + } + + pub fn add(&self, region: &Self) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_union(&mut new, self, region); + } + new + } + + pub fn subtract(&self, region: &Self) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_subtract(&mut new, self, region); + } + new + } +} + +impl Clone for Region { + fn clone(&self) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_copy(&mut new, self); + } + new + } +} + +impl Drop for Region { + fn drop(&mut self) { + unsafe { + pixman_region32_fini(self); + } + } +} diff --git a/src/sighand.rs b/src/sighand.rs new file mode 100644 index 00000000..5349940d --- /dev/null +++ b/src/sighand.rs @@ -0,0 +1,75 @@ +use crate::event_loop::{EventLoopDispatcher, EventLoopId, EventLoopRef}; +use crate::EventLoopError; +use std::error::Error; +use std::rc::Rc; +use thiserror::Error; +use uapi::{c, Errno, OwnedFd}; + +#[derive(Debug, Error)] +pub enum SighandError { + #[error("The signal fd is in an error state")] + ErrorEvent, + #[error("Could not read from the signal fd")] + ReadFailed(#[source] std::io::Error), + #[error("Could not block the signalfd signals")] + BlockFailed(#[source] std::io::Error), + #[error("Could not create a signalfd")] + CreateFailed(#[source] std::io::Error), + #[error("The event loop caused an error")] + EventLoopError(#[from] EventLoopError), +} + +pub fn install(el: &EventLoopRef) -> Result<(), SighandError> { + let mut set: c::sigset_t = uapi::pod_zeroed(); + uapi::sigaddset(&mut set, c::SIGINT).unwrap(); + if let Err(e) = uapi::pthread_sigmask(c::SIG_BLOCK, Some(&set), None) { + return Err(SighandError::BlockFailed(e.into())); + } + let fd = match uapi::signalfd_new(&set, c::SFD_CLOEXEC | c::SFD_NONBLOCK) { + Ok(fd) => fd, + Err(e) => return Err(SighandError::CreateFailed(e.into())), + }; + let id = el.id()?; + let sh = Rc::new(Sighand { + fd, + id, + el: el.clone(), + }); + el.insert(id, Some(sh.fd.raw()), c::EPOLLIN, sh)?; + Ok(()) +} + +struct Sighand { + fd: OwnedFd, + id: EventLoopId, + el: EventLoopRef, +} + +impl EventLoopDispatcher for Sighand { + fn dispatch(&self, events: i32) -> Result<(), Box> { + if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { + return Err(Box::new(SighandError::ErrorEvent)); + } + let mut sigfd: c::signalfd_siginfo = uapi::pod_zeroed(); + loop { + if let Err(e) = uapi::read(self.fd.raw(), &mut sigfd) { + match e { + Errno(c::EAGAIN) => break, + _ => return Err(Box::new(SighandError::ReadFailed(e.into()))), + } + } + log::info!("Received signal {}", sigfd.ssi_signo); + if sigfd.ssi_signo == c::SIGINT as _ { + log::info!("Exiting"); + self.el.stop(); + } + } + Ok(()) + } +} + +impl Drop for Sighand { + fn drop(&mut self) { + let _ = self.el.remove(self.id); + } +} diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 00000000..c0832fc9 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,14 @@ +use crate::async_engine::AsyncEngine; +use crate::event_loop::EventLoopRef; +use crate::globals::Globals; +use crate::utils::numcell::NumCell; +use crate::wl_client::WlClients; +use std::rc::Rc; + +pub struct State { + pub eng: Rc, + pub el: EventLoopRef, + pub clients: WlClients, + pub next_name: NumCell, + pub globals: Globals, +} diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 00000000..67ee0e45 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,97 @@ +use std::cmp::Ordering; +use std::fmt::{Debug, Formatter}; +use std::ops::{Add, Sub}; +use std::time::Duration; +use thiserror::Error; +use uapi::c; + +#[derive(Debug, Error)] +pub enum TimeError { + #[error("clock_gettime failed: {0}")] + ClockGettime(std::io::Error), +} + +#[derive(Copy, Clone)] +pub struct Time(pub c::timespec); + +impl Debug for Time { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Time") + .field("tv_sec", &self.0.tv_sec) + .field("tv_nsec", &self.0.tv_nsec) + .finish() + } +} + +impl Time { + pub fn now() -> Result { + let mut time = uapi::pod_zeroed(); + if let Err(e) = uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut time) { + return Err(TimeError::ClockGettime(e.into())); + } + Ok(Self(time)) + } + + pub fn round_to_ms(self) -> Time { + if self.0.tv_nsec > 999_000_000 { + Time(c::timespec { + tv_sec: self.0.tv_sec + 1, + tv_nsec: 0, + }) + } else { + Time(c::timespec { + tv_sec: self.0.tv_sec, + tv_nsec: (self.0.tv_nsec + 999_999) / 1_000_000 * 1_000_000, + }) + } + } +} + +impl Eq for Time {} + +impl PartialEq for Time { + fn eq(&self, other: &Self) -> bool { + self.0.tv_sec == other.0.tv_sec && self.0.tv_nsec == other.0.tv_nsec + } +} + +impl Ord for Time { + fn cmp(&self, other: &Self) -> Ordering { + self.0 + .tv_sec + .cmp(&other.0.tv_sec) + .then_with(|| self.0.tv_nsec.cmp(&other.0.tv_nsec)) + } +} + +impl PartialOrd for Time { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Sub