#![warn(missing_docs)]
pub(crate) const PROTOCOL_VERSION: u32 = 1;
pub use hyprwire_core as core;
pub mod client;
pub mod error;
pub use error::Error;
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) mod helpers;
pub use helpers::reset_trace_cache;
pub mod implementation;
pub(crate) mod message;
pub mod server;
pub(crate) mod socket;
#[cfg(feature = "log")]
#[allow(unused_imports)]
use log::{debug as log_debug, error as log_error, info as log_info, warn as log_warn};
#[cfg(not(feature = "log"))]
#[allow(unused_imports)]
use std::{
eprintln as log_debug, eprintln as log_error, eprintln as log_info, eprintln as log_warn,
};
use implementation::object as impl_object;
use std::os::fd::AsRawFd;
use std::os::unix::net;
use std::{cell, io, rc, sync, time};
pub(crate) struct ConnectionState {
pub(crate) error: cell::Cell<bool>,
pub(crate) stream: net::UnixStream,
pub(crate) impls:
rc::Rc<cell::RefCell<Vec<Box<dyn implementation::server::ProtocolImplementations>>>>,
}
impl ConnectionState {
pub(crate) fn new(
stream: net::UnixStream,
impls: rc::Rc<cell::RefCell<Vec<Box<dyn implementation::server::ProtocolImplementations>>>>,
) -> Self {
Self {
error: cell::Cell::new(false),
stream,
impls,
}
}
pub(crate) fn send_message(&self, message: &dyn hyprwire_core::message::Message) {
trace! { crate::log_debug!("[hw] trace: [{} @ {:.3}] -> {}", self.stream.as_raw_fd(), steady_millis(), message.parse_data()) };
let buf = message.data();
let iov = [io::IoSlice::new(buf)];
let fds = message.fds();
let borrowed: Vec<rustix::fd::BorrowedFd<'_>> = fds
.iter()
.map(|&fd| unsafe { rustix::fd::BorrowedFd::borrow_raw(fd) })
.collect();
let mut space =
vec![std::mem::MaybeUninit::<u8>::uninit(); rustix::cmsg_space!(ScmRights(fds.len()))];
let mut ancillary = rustix::net::SendAncillaryBuffer::new(&mut space);
ancillary.push(rustix::net::SendAncillaryMessage::ScmRights(&borrowed));
loop {
match rustix::net::sendmsg(
&self.stream,
&iov,
&mut ancillary,
rustix::net::SendFlags::empty(),
) {
Ok(_) => break,
Err(e) if e == rustix::io::Errno::AGAIN => {
let mut pfd = [rustix::event::PollFd::new(
&self.stream,
rustix::event::PollFlags::OUT,
)];
if let Err(e) = rustix::event::poll(&mut pfd, None) {
crate::log_error!(
"[{} @ {:.3}] poll error during send_message: {e}",
self.stream.as_raw_fd(),
steady_millis(),
);
break;
}
}
Err(_) => break,
}
}
}
}
#[macro_export]
macro_rules! include_protocol {
($name:expr) => {
include!(concat!(env!("OUT_DIR"), "/", $name, ".rs"));
};
}
#[doc(hidden)]
pub trait Object: Sized {
type Event<'a>;
const NAME: &str;
fn from_object<D: Dispatch<Self> + 'static>(object: rc::Rc<dyn impl_object::Object>) -> Self;
}
pub trait Dispatch<O: crate::Object> {
fn event(&mut self, object: &O, event: <O as crate::Object>::Event<'_>);
}
#[macro_export]
macro_rules! delegate_noop {
($(@< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? $dispatch_from:ty : $interface:ty) => {
impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::Dispatch<$interface> for $dispatch_from {
fn event(&mut self, _: &$interface, _: <$interface as $crate::Object>::Event<'_>) {
unreachable!();
}
}
};
($(@< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? $dispatch_from:ty : ignore $interface:ty) => {
impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::Dispatch<$interface> for $dispatch_from {
fn event(&mut self, _: &$interface, _: <$interface as $crate::Object>::Event<'_>) {
}
}
};
}
static START: sync::OnceLock<time::Instant> = sync::OnceLock::new();
pub(crate) fn steady_millis() -> f64 {
let start = START.get_or_init(time::Instant::now);
start.elapsed().as_nanos() as f64 / 1_000_000.0
}