#![allow(incomplete_features)]
#![feature(type_alias_impl_trait, trait_upcasting)]
#![warn(
missing_debug_implementations,
missing_copy_implementations,
missing_docs,
rust_2018_idioms,
single_use_lifetimes
)]
use futures_lite::stream::StreamExt;
use hashbrown::{hash_map, HashMap};
use thiserror::Error;
pub mod client;
pub mod error;
pub mod events;
pub mod globals;
pub mod objects;
pub mod provide_any;
pub mod server;
mod utils;
#[doc(hidden)]
pub mod __private {
pub use runa_io::traits::{
buf::AsyncBufReadWithFd,
de::{Deserialize, Error as DeserError},
ser::Serialize,
WriteMessage,
};
pub use runa_wayland_protocols::wayland::wl_display;
pub use runa_wayland_types as types;
pub use static_assertions::assert_impl_all;
}
#[derive(Error, Debug)]
pub enum ConnectionManagerError<SE, LE> {
#[error("Server error: {0}")]
Server(#[source] SE),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Listener error: {0}")]
Listener(#[source] LE),
}
#[derive(Debug)]
pub struct ConnectionManager<S, Ctx> {
listeners: S,
ctx: Ctx,
}
impl<S, Ctx, I, E> ConnectionManager<S, Ctx>
where
S: futures_lite::stream::Stream<Item = Result<I, E>> + Unpin,
Ctx: crate::server::traits::Server<Conn = I>,
{
pub fn new(listeners: S, ctx: Ctx) -> Self {
Self { listeners, ctx }
}
pub async fn run(&mut self) -> Result<(), ConnectionManagerError<Ctx::Error, E>> {
while let Some(conn) = self.listeners.next().await {
let conn = conn.map_err(ConnectionManagerError::Listener)?;
self.ctx
.new_connection(conn)
.map_err(ConnectionManagerError::Server)?;
}
Ok(())
}
}
#[derive(Error, Debug)]
pub enum ListenerError {
#[error("I/O error: {0}")]
SmolIo(#[from] std::io::Error),
#[error("xdg base directory error: {0}")]
XdgBaseDirectory(#[from] xdg::BaseDirectoriesError),
#[error("unix error: {0}")]
Unix(#[from] rustix::io::Errno),
}
#[derive(Debug)]
pub struct FlockGuard {
fd: rustix::fd::OwnedFd,
}
impl Drop for FlockGuard {
fn drop(&mut self) {
use rustix::fd::AsFd;
rustix::fs::flock(self.fd.as_fd(), rustix::fs::FlockOperation::Unlock).unwrap();
}
}
pub fn wayland_listener(
display: &str,
) -> Result<(std::os::unix::net::UnixListener, FlockGuard), ListenerError> {
use rustix::fd::AsFd;
let xdg_dirs = xdg::BaseDirectories::new()?;
let path = xdg_dirs.place_runtime_file(display)?;
let lock_path = xdg_dirs.place_runtime_file(format!("{display}.lock"))?;
let lock = rustix::fs::openat(
rustix::fs::cwd(),
lock_path,
rustix::fs::OFlags::CREATE,
rustix::fs::Mode::RUSR | rustix::fs::Mode::WUSR,
)?;
rustix::fs::flock(
lock.as_fd(),
rustix::fs::FlockOperation::NonBlockingLockExclusive,
)?;
let _ = std::fs::remove_file(&path);
Ok((std::os::unix::net::UnixListener::bind(path)?, FlockGuard {
fd: lock,
}))
}
pub fn wayland_listener_auto(
) -> Result<(std::os::unix::net::UnixListener, FlockGuard), ListenerError> {
const MAX_DISPLAYNO: u32 = 32;
let mut last_err = None;
for i in 0..MAX_DISPLAYNO {
let display = format!("wayland-{i}");
match wayland_listener(&display) {
Ok((listener, guard)) => return Ok((listener, guard)),
e @ Err(_) => {
last_err = Some(e);
},
}
}
last_err.unwrap()
}
#[allow(missing_docs)]
pub trait Serial {
type Data;
type Iter<'a>: Iterator<Item = (u32, &'a Self::Data)>
where
Self: 'a,
Self::Data: 'a;
fn next_serial(&mut self, data: Self::Data) -> u32;
fn get(&self, serial: u32) -> Option<&Self::Data>;
fn iter(&self) -> Self::Iter<'_>;
fn expire(&mut self, serial: u32) -> bool;
}
struct IdAlloc<D> {
next: u32,
data: HashMap<u32, D>,
}
impl<D> Default for IdAlloc<D> {
fn default() -> Self {
Self {
next: 1,
data: HashMap::new(),
}
}
}
impl<D> std::fmt::Debug for IdAlloc<D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct DebugMap<'a, K, V>(&'a HashMap<K, V>);
impl<K: std::fmt::Debug, V> std::fmt::Debug for DebugMap<'_, K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_set().entries(self.0.keys()).finish()
}
}
f.debug_struct("IdAlloc")
.field("next", &self.next)
.field("data", &DebugMap(&self.data))
.finish()
}
}
impl<D> Serial for IdAlloc<D> {
type Data = D;
type Iter<'a> = <&'a Self as IntoIterator>::IntoIter where Self: 'a;
fn next_serial(&mut self, data: Self::Data) -> u32 {
loop {
let id = self.next;
self.next += 1;
match self.data.entry(id) {
hash_map::Entry::Vacant(e) => {
e.insert(data);
break id
},
hash_map::Entry::Occupied(_) => (),
}
}
}
fn get(&self, serial: u32) -> Option<&Self::Data> {
self.data.get(&serial)
}
fn expire(&mut self, serial: u32) -> bool {
self.data.remove(&serial).is_some()
}
fn iter(&self) -> Self::Iter<'_> {
self.into_iter()
}
}
#[doc(hidden)]
pub type IdIter<'a, D>
where
D: 'a,
= impl Iterator<Item = (u32, &'a D)> + 'a;
impl<'a, D: 'a> IntoIterator for &'a IdAlloc<D> {
type IntoIter = IdIter<'a, D> where Self: 'a;
type Item = (u32, &'a D);
fn into_iter(self) -> IdIter<'a, D> {
self.data.iter().map(|(k, v)| (*k, v))
}
}
#[macro_export]
macro_rules! globals {
(
__internal, $ctx:ty, $(#[$attr:meta])* ($($vis:tt)?) enum $N:ident { $($var:ident($f:ty)),+ $(,)? }
) => {
$(#[$attr])*
$($vis)? enum $N {
$($var($f)),*
}
$(
impl From<$f> for $N {
fn from(f: $f) -> Self {
$N::$var(f)
}
}
)*
impl $crate::globals::AnyGlobal for $N {
type Object = <$ctx as $crate::client::traits::Client>::Object;
fn interface(&self) -> &'static str {
match self {
$(
$N::$var(f) => <$f as $crate::globals::MonoGlobal>::INTERFACE,
)*
}
}
fn version(&self) -> u32 {
match self {
$(
$N::$var(f) => <$f as $crate::globals::MonoGlobal>::VERSION,
)*
}
}
fn new_object(&self) -> Self::Object {
match self {
$(
$N::$var(f) => <$f as $crate::globals::MonoGlobal>::new_object().into(),
)*
}
}
fn cast<T: 'static>(&self) -> Option<&T> {
match self {
$(
$N::$var(f) => (f as &dyn ::std::any::Any).downcast_ref::<T>(),
)*
}
}
}
impl $crate::globals::Bind<$ctx> for $N {
type BindFut<'a> = impl std::future::Future<Output = std::io::Result<()>> + 'a
where
Self: 'a;
fn bind<'a>(&'a self, client: &'a mut $ctx, object_id: u32) -> Self::BindFut<'a> {
async move {
Ok(match self {$(
$N::$var(f) => <$f as $crate::globals::Bind<$ctx>>::bind(f, client, object_id).await?,
)*})
}
}
}
impl $N {
$($vis)? fn globals() -> impl Iterator<Item = $N> {
[$(<$f as $crate::globals::MonoGlobal>::MAYBE_DEFAULT.map($N::$var)),*].into_iter().flatten()
}
}
};
(
type ClientContext = $ctx:ty;
$(#[$attr:meta])* pub enum $N:ident { $($var:ident($f:ty)),+ $(,)? }
) => {
$crate::globals!(
__internal,
$ctx,
$(#[$attr])* $(#[$attr])* (pub) enum $N { $($var($f)),* }
);
};
(
type ClientContext = $ctx:ty;
$(#[$attr:meta])* enum $N:ident { $($var:ident($f:ty)),+ $(,)? }
) => {
$crate::globals!(
__internal,
$ctx,
$(#[$attr])* $(#[$attr])* () enum $N { $($var($f)),* }
);
};
}