wl-proxy 0.1.2

Wayland connection proxy
Documentation
use {
    error_reporter::Report,
    std::{
        io,
        os::fd::{AsRawFd, BorrowedFd, OwnedFd},
        rc::Rc,
    },
    thiserror::Error,
    uapi::{Errno, c},
};

#[cfg(test)]
mod tests;

#[derive(Debug, Error)]
pub(crate) enum PollError {
    #[error("could not create epoll fd")]
    Create(#[source] io::Error),
    #[error("could not read epoll events")]
    Read(#[source] io::Error),
    #[error("could not register socket with epoll")]
    Add(#[source] io::Error),
    #[error("could not update epoll interests")]
    Update(#[source] io::Error),
}

pub(crate) const MAX_EVENTS: usize = 16;

pub(crate) const READABLE: u32 = c::EPOLLIN as u32;
pub(crate) const WRITABLE: u32 = c::EPOLLOUT as u32;
pub(crate) const ERROR: u32 = (c::EPOLLERR | c::EPOLLHUP) as u32;

const ONESHOT: u32 = c::EPOLLONESHOT as u32;
const ET: u32 = c::EPOLLET as u32;

const ALL: u32 = READABLE | WRITABLE | ERROR | ONESHOT | ET;

#[derive(Copy, Clone, Default)]
pub(crate) struct PollEvent {
    pub u64: u64,
    pub events: u32,
}

pub(crate) struct Poller {
    epoll: Rc<OwnedFd>,
}

impl Poller {
    pub(crate) fn new() -> Result<Self, PollError> {
        let epoll =
            uapi::epoll_create1(c::EPOLL_CLOEXEC).map_err(|e| PollError::Create(e.into()))?;
        Ok(Self {
            epoll: Rc::new(epoll.into()),
        })
    }

    pub(crate) fn fd(&self) -> &Rc<OwnedFd> {
        &self.epoll
    }

    pub(crate) fn read_events(
        &self,
        timeout: c::c_int,
        events: &mut [PollEvent; MAX_EVENTS],
    ) -> Result<usize, PollError> {
        loop {
            let mut epe = [c::epoll_event { events: 0, u64: 0 }; MAX_EVENTS];
            let res = uapi::epoll_wait(self.epoll.as_raw_fd(), &mut epe, timeout);
            let n = match res {
                Ok(n) => n,
                Err(Errno(c::EINTR)) => continue,
                Err(e) => return Err(PollError::Read(e.into())),
            };
            for i in 0..n {
                let epe = &epe[i];
                let ev = &mut events[i];
                ev.u64 = epe.u64;
                ev.events = epe.events & ALL;
            }
            return Ok(n);
        }
    }

    pub(crate) fn register(&self, id: u64, fd: BorrowedFd<'_>) -> Result<(), PollError> {
        let event = c::epoll_event {
            events: ONESHOT,
            u64: id,
        };
        uapi::epoll_ctl(
            self.epoll.as_raw_fd(),
            c::EPOLL_CTL_ADD,
            fd.as_raw_fd(),
            Some(&event),
        )
        .map_err(|e| PollError::Add(e.into()))
    }

    pub(crate) fn unregister(&self, fd: BorrowedFd<'_>) {
        let res = uapi::epoll_ctl(
            self.epoll.as_raw_fd(),
            c::EPOLL_CTL_DEL,
            fd.as_raw_fd(),
            None,
        );
        if let Err(e) = res {
            log::warn!(
                "Could not remove a file descriptor from epoll: {}",
                Report::new(io::Error::from(e)),
            );
        }
    }

    pub(crate) fn update_interests(
        &self,
        id: u64,
        fd: BorrowedFd<'_>,
        events: u32,
    ) -> Result<(), PollError> {
        let event = c::epoll_event {
            events: events | ONESHOT,
            u64: id,
        };
        uapi::epoll_ctl(
            self.epoll.as_raw_fd(),
            c::EPOLL_CTL_MOD,
            fd.as_raw_fd(),
            Some(&event),
        )
        .map_err(|e| PollError::Update(e.into()))
    }

    pub(crate) fn register_edge_triggered(
        &self,
        id: u64,
        fd: BorrowedFd<'_>,
        events: u32,
    ) -> Result<(), PollError> {
        let event = c::epoll_event {
            events: events | ET,
            u64: id,
        };
        uapi::epoll_ctl(
            self.epoll.as_raw_fd(),
            c::EPOLL_CTL_ADD,
            fd.as_raw_fd(),
            Some(&event),
        )
        .map_err(|e| PollError::Add(e.into()))
    }
}