use crate::types::{
NewId, ObjectId, WlFixed, WlSlice, WlStr, i32_slice_to_u8_mut, u32_slice_to_u8,
u32_slice_to_u8_mut,
};
use core::{mem::MaybeUninit, num::NonZeroU32};
use rustix::fd::{AsFd, AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use rustix::io;
const WL_MAX_MESSAGE_SIZE: usize = 4096;
const WL_MAX_MESSAGE_SIZE_U32: usize = WL_MAX_MESSAGE_SIZE / core::mem::size_of::<u32>();
type WaylandBuffer = [u32; WL_MAX_MESSAGE_SIZE_U32];
#[derive(Debug)]
pub struct Payload {
bytes: WaylandBuffer,
fds: [RawFd; 32],
cur: u16,
cur_fd: u8,
}
#[derive(Debug)]
pub struct Receiver {
payload: Payload,
}
#[derive(Debug)]
pub struct Messages<'a> {
sender_id: ObjectId,
op: u16,
bytes_received: u16,
fds_received: u8,
payload: &'a mut Payload,
}
impl Payload {
fn new() -> Self {
Self {
bytes: [0; WL_MAX_MESSAGE_SIZE_U32],
fds: [0; 32],
cur: 0,
cur_fd: 0,
}
}
#[inline]
#[must_use]
pub fn bytes(&self) -> &[u32] {
self.bytes.as_slice()
}
#[inline]
#[must_use]
pub fn fds(&self) -> &[RawFd] {
self.fds.as_slice()
}
#[inline]
#[must_use]
unsafe fn get_cur_byte_offset(&self, offset: isize) -> u32 {
let i = (self.cur as isize + offset) as usize;
unsafe { *self.bytes.get_unchecked(i) }
}
#[inline]
#[must_use]
unsafe fn get_cur_fd(&self) -> i32 {
unsafe { *self.fds.get_unchecked(self.cur_fd as usize) }
}
}
impl Receiver {
#[inline]
#[must_use]
pub(crate) fn new() -> Self {
Self {
payload: Payload::new(),
}
}
#[inline]
pub fn recv<'a>(
&'a mut self,
wayland_fd: &impl rustix::fd::AsFd,
) -> Result<Messages<'a>, Error> {
use rustix::net;
let mut ancillary_buf = [MaybeUninit::uninit(); 32];
let iov = io::IoSliceMut::new(u32_slice_to_u8_mut(
&mut self.payload.bytes[self.payload.cur as usize..],
));
let mut control = net::RecvAncillaryBuffer::new(i32_slice_to_u8_mut(&mut ancillary_buf));
let bytes = net::recvmsg(
wayland_fd,
&mut [iov],
&mut control,
net::RecvFlags::empty(),
)
.map_err(Error::RustixIoErrno)?;
let mut fd_index = 0;
for msg in control.drain() {
if let net::RecvAncillaryMessage::ScmRights(iter) = msg {
let i = self.payload.cur_fd as usize;
for fd in iter {
*unsafe { self.payload.fds.get_unchecked_mut(i + fd_index) } = fd.into_raw_fd();
fd_index += 1;
}
}
}
let bytes_received = self.payload.cur + (bytes.bytes / core::mem::size_of::<u32>()) as u16;
let fds_received = self.payload.cur_fd + fd_index as u8;
self.payload.cur = 0;
self.payload.cur_fd = 0;
Ok(Messages {
sender_id: ObjectId::new(NonZeroU32::new(1).unwrap()),
op: 0,
bytes_received,
fds_received,
payload: &mut self.payload,
})
}
#[inline]
#[must_use]
pub fn payload(&self) -> &Payload {
&self.payload
}
}
impl Default for Receiver {
fn default() -> Self {
Self::new()
}
}
impl Iterator for Messages<'_> {
type Item = Result<ObjectId, Error>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.payload.cur + 1 >= self.bytes_received {
return None;
}
let byte = unsafe { self.payload.get_cur_byte_offset(1) };
let len = byte >> 16;
assert!(
len <= WL_MAX_MESSAGE_SIZE as u32,
"Received wayland message is too large: {len}. WL_MAX_MESSAGE_SIZE is {WL_MAX_MESSAGE_SIZE}"
);
if self.payload.cur + len as u16 / 4 > self.bytes_received {
return None;
}
match ObjectId::try_new(unsafe { self.payload.get_cur_byte_offset(0) }) {
Ok(sender_id) => {
self.sender_id = sender_id;
self.op = (byte & 0xFFFF) as u16;
self.payload.cur += 2;
Some(Ok(sender_id))
}
Err(_) => Some(Err(Error::NullObjectId)),
}
}
}
impl Messages<'_> {
#[inline]
#[doc(hidden)]
pub fn validate_len<const N: usize>(&self, is_array_type: [bool; N]) -> Result<(), Error> {
let mut sum = 0;
let mut i = 0;
while i < is_array_type.len() {
if !is_array_type[i] {
sum += 1;
} else {
let len = unsafe { self.payload.get_cur_byte_offset(sum as isize) as usize };
sum += 1 + ((len + 3) >> 2);
if sum + is_array_type.len() - i > self.bytes_received as usize {
return Err(Error::InvalidArrayOrStringLength);
}
}
i += 1;
}
Ok(())
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_fd(&mut self) -> OwnedFd {
let fd = unsafe { self.payload.get_cur_fd() };
self.payload.cur_fd += 1;
unsafe { OwnedFd::from_raw_fd(fd) }
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn sender_id(&self) -> ObjectId {
self.sender_id
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn op(&self) -> u16 {
self.op
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_i32(&mut self) -> i32 {
self.next_u32() as i32
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_u32(&mut self) -> u32 {
self.payload.cur += 1;
unsafe { self.payload.get_cur_byte_offset(-1) }
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_fixed(&mut self) -> WlFixed {
WlFixed(self.next_i32())
}
#[inline]
#[doc(hidden)]
pub fn next_string<'a>(&mut self) -> Result<&'a str, Error> {
let len = unsafe { self.payload.get_cur_byte_offset(0) as usize };
unsafe {
let ptr = self
.payload
.bytes()
.as_ptr()
.add(self.payload.cur as usize + 1);
let cast = core::slice::from_raw_parts(ptr.cast(), len - 1);
self.payload.cur += 1 + ((len + 3) >> 2) as u16;
core::str::from_utf8(cast).map_err(Error::InvalidUTF8FromWire)
}
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_object(&mut self) -> Option<ObjectId> {
ObjectId::try_new(self.next_u32()).ok()
}
#[inline]
#[doc(hidden)]
pub fn next_new_specified_id(&mut self) -> Result<ObjectId, Error> {
ObjectId::try_new(self.next_u32())
}
#[inline]
#[doc(hidden)]
pub fn next_new_unspecified_id<'a>(&mut self) -> Result<NewId<'a>, Error> {
let interface = self.next_string()?;
let version = self.next_u32();
let id = self.next_new_specified_id()?;
Ok(NewId {
id,
interface,
version,
})
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_array<'a>(&mut self) -> &'a [u8] {
let len = unsafe { self.payload.get_cur_byte_offset(0) as usize };
unsafe {
let ptr = self
.payload
.bytes()
.as_ptr()
.add(self.payload.cur as usize + 1); self.payload.cur += 1 + ((len + 3) >> 2) as u16;
core::slice::from_raw_parts(ptr.cast(), len)
}
}
}
impl Drop for Messages<'_> {
fn drop(&mut self) {
let remaining_byte_len = (self.bytes_received).saturating_sub(self.payload.cur);
let remaining_fds_len = (self.fds_received).saturating_sub(self.payload.cur_fd);
unsafe {
let (lo, hi) = self
.payload
.bytes
.split_at_mut_unchecked(self.payload.cur as usize);
lo.as_mut_ptr()
.copy_from(hi.as_ptr(), remaining_byte_len as usize)
}
unsafe {
let (lo, hi) = self
.payload
.fds
.split_at_mut_unchecked(self.payload.cur_fd as usize);
lo.as_mut_ptr()
.copy_from(hi.as_ptr(), remaining_fds_len as usize)
}
self.payload.cur = remaining_byte_len;
self.payload.cur_fd = remaining_fds_len;
}
}
pub struct MessageBuilder {
msg: WaylandBuffer,
fds: [RawFd; 32],
msg_len: u16,
fds_len: u8,
}
impl MessageBuilder {
#[inline]
#[must_use]
pub(crate) fn new() -> Self {
Self {
msg: [0; WL_MAX_MESSAGE_SIZE_U32],
fds: [0; 32],
msg_len: 0,
fds_len: 0,
}
}
#[inline]
#[doc(hidden)]
pub fn ensure_space<Fd: AsFd>(
&mut self,
wayland_fd: &Fd,
byte_size: usize,
fds: usize,
) -> io::Result<()> {
if byte_size > WL_MAX_MESSAGE_SIZE_U32 || fds > self.fds.len() {
return Err(io::Errno::OVERFLOW);
}
if byte_size + self.msg_len as usize >= self.msg.len()
|| fds + self.fds_len as usize >= self.fds.len()
{
self.flush(wayland_fd)?;
}
Ok(())
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_header(&mut self, sender_id: ObjectId, op: u16, len: u16) {
unsafe {
self.add_u32(sender_id.get().get());
self.add_u32((op as u32) | ((len as u32) << 16));
}
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_i32(&mut self, i: i32) {
unsafe { self.add_u32(i as u32) };
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_u32(&mut self, u: u32) {
*unsafe { self.msg.get_unchecked_mut(self.msg_len as usize) } = u;
self.msg_len += 1;
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_fixed(&mut self, fixed: WlFixed) {
unsafe { self.add_i32(fixed.0) };
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_string(&mut self, s: &str) {
let str = WlStr(s);
let bytes = str.encode(unsafe { self.msg.get_unchecked_mut(self.msg_len as usize..) });
self.msg_len += (bytes >> 2) as u16;
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_object(&mut self, object_id: Option<ObjectId>) {
unsafe {
match object_id {
Some(id) => self.add_u32(id.get().get()),
None => self.add_u32(0),
}
}
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_new_specified_id(&mut self, object_id: ObjectId) {
unsafe { self.add_u32(object_id.get().get()) };
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_new_unspecified_id(
&mut self,
object_id: ObjectId,
interface: &str,
version: u32,
) {
unsafe {
self.add_string(interface);
self.add_u32(version);
self.add_new_specified_id(object_id);
}
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_array(&mut self, array: &[u8]) {
let slice = WlSlice(array);
let bytes = slice.encode(unsafe { self.msg.get_unchecked_mut(self.msg_len as usize..) });
self.msg_len += (bytes >> 2) as u16;
}
#[inline]
#[doc(hidden)]
pub unsafe fn add_fd<'a, 'b>(&'a mut self, fd: &'b impl AsRawFd) -> io::Result<()>
where
'b: 'a,
{
*unsafe { self.fds.get_unchecked_mut(self.fds_len as usize) } = fd.as_raw_fd();
self.fds_len += 1;
Ok(())
}
#[doc(hidden)]
pub fn flush<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd) -> io::Result<()> {
use rustix::net;
const FLAGS: net::SendFlags = net::SendFlags::NOSIGNAL;
let Self {
msg,
fds,
msg_len,
fds_len,
} = self;
let msg_slice = u32_slice_to_u8(unsafe { msg.get_unchecked(0..*msg_len as usize) });
if *fds_len == 0 {
net::send(wayland_fd, msg_slice, FLAGS)?;
} else {
let iov = io::IoSlice::new(msg_slice);
let mut space = [MaybeUninit::<u8>::uninit(); rustix::cmsg_space!(ScmRights(32))];
let mut control = net::SendAncillaryBuffer::new(&mut space);
control.push(net::SendAncillaryMessage::ScmRights(unsafe {
core::mem::transmute::<&[i32], &[rustix::fd::BorrowedFd<'_>]>(
fds.get_unchecked(..*fds_len as usize),
)
}));
net::sendmsg(wayland_fd, &[iov], &mut control, FLAGS)?;
}
*msg_len = 0;
*fds_len = 0;
Ok(())
}
}
impl Default for MessageBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub enum Error {
UnrecognizedEventOpCode(&'static str, u16),
InvalidEnumDiscriminant(&'static str, u32),
RustixIoErrno(io::Errno),
InvalidUTF8FromWire(core::str::Utf8Error),
InvalidArrayOrStringLength,
NullObjectId,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Error::UnrecognizedEventOpCode(event, opcode) => {
write!(f, "unrecognized opcode {opcode} for event {event}")
}
Error::InvalidEnumDiscriminant(enum_name, number) => {
write!(f, "invalid discriminant {number} for enum {enum_name}")
}
Error::RustixIoErrno(errno) => core::fmt::Display::fmt(errno, f),
Error::InvalidUTF8FromWire(utf8_error) => write!(
f,
"received invalid utf8 string from the wire: {utf8_error}"
),
Error::NullObjectId => f.write_str("received unexpected null ObjectId"),
Error::InvalidArrayOrStringLength => f.write_str(
"received an array or string from the\
wayland protocol larger than the message length itself",
),
}
}
}
impl core::error::Error for Error {}
#[cfg(test)]
mod tests {
extern crate std;
use core::{ffi::c_int, mem::ManuallyDrop, num::NonZeroU32};
use std::{path::Path, string::ToString};
use tempdir::TempDir;
use super::*;
const DUMMY_FD: ManuallyDrop<OwnedFd> =
unsafe { ManuallyDrop::new(std::mem::transmute::<i32, OwnedFd>(c_int::MAX)) };
const DUMMY_OBJ: ObjectId = ObjectId::new(NonZeroU32::new(1).unwrap());
fn bind_dummy_wayland_socket<P: AsRef<Path>>(tmp_dir: &TempDir, path: P) -> OwnedFd {
let path = tmp_dir.path().join(path);
let unix_addr =
rustix::net::SocketAddrUnix::new(path.display().to_string().as_str()).unwrap();
let socket = rustix::net::socket_with(
rustix::net::AddressFamily::UNIX,
rustix::net::SocketType::STREAM,
rustix::net::SocketFlags::CLOEXEC,
None,
)
.unwrap();
rustix::net::bind(&socket, &unix_addr).unwrap();
rustix::net::listen(&socket, 0).unwrap();
socket
}
fn connect_dummy_wayland_socket<P: AsRef<Path>>(tmp_dir: &TempDir, path: P) -> OwnedFd {
let path = tmp_dir.path().join(path);
let stream = std::os::unix::net::UnixStream::connect(path).unwrap();
stream.set_nonblocking(true).unwrap();
let fd = std::os::fd::IntoRawFd::into_raw_fd(stream);
unsafe { OwnedFd::from_raw_fd(fd) }
}
#[test]
fn msg_builder_header() {
let mut builder = MessageBuilder::new();
unsafe { builder.add_header(DUMMY_OBJ, 0, 8) };
assert_eq!(
&builder.msg[..builder.msg_len as usize],
&[DUMMY_OBJ.get().get(), 8 << 16]
);
unsafe { builder.add_header(DUMMY_OBJ, 1, 8) };
assert_eq!(
&builder.msg[..builder.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
8 << 16,
DUMMY_OBJ.get().get(),
1 | (8 << 16)
]
);
}
#[test]
fn msg_builder_arrays() {
let mut builder = MessageBuilder::new();
unsafe { builder.add_header(DUMMY_OBJ, 1, 8 + 4 * 5 + 4 * 4) };
unsafe { builder.add_array(&[]) };
unsafe { builder.add_array(&[1]) };
unsafe { builder.add_array(&[1, 2]) };
unsafe { builder.add_array(&[1, 2, 3]) };
unsafe { builder.add_array(&[1, 2, 3, 4]) };
assert_eq!(builder.msg_len, (8 + 4 * 5 + 4 * 4) / 4);
assert_eq!(
&builder.msg[..builder.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
1 | ((8 + 4 + 8 + 8 + 8 + 8) << 16),
0, 1, u32::from_ne_bytes([1, 0, 0, 0]),
2, u32::from_ne_bytes([1, 2, 0, 0]),
3, u32::from_ne_bytes([1, 2, 3, 0]),
4, u32::from_ne_bytes([1, 2, 3, 4]),
]
);
}
#[test]
fn msg_builder_strings() {
let mut builder = MessageBuilder::new();
unsafe { builder.add_header(DUMMY_OBJ, 1, 8 + 4 * 5 + 4 * 4 + 8) };
unsafe { builder.add_string("") };
unsafe { builder.add_string("1") };
unsafe { builder.add_string("12") };
unsafe { builder.add_string("123") };
unsafe { builder.add_string("1234") };
assert_eq!(builder.msg_len, (8 + 4 * 5 + 4 * 4 + 8) / 4);
assert_eq!(
&builder.msg[..builder.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
1 | ((8 + 8 + 8 + 8 + 8 + 12) << 16),
1, 0,
2, u32::from_ne_bytes([b'1', 0, 0, 0]),
3, u32::from_ne_bytes([b'1', b'2', 0, 0]),
4, u32::from_ne_bytes([b'1', b'2', b'3', 0]),
5, u32::from_ne_bytes([b'1', b'2', b'3', b'4']),
0,
]
);
}
#[test]
fn msg_builder_scalars() {
let mut builder = MessageBuilder::new();
unsafe { builder.add_header(DUMMY_OBJ, 1, 28) };
unsafe { builder.add_u32(1) };
unsafe { builder.add_i32(2) };
unsafe { builder.add_i32(-1) };
unsafe { builder.add_fixed(WlFixed::from(1i32)) };
unsafe { builder.add_object(Some(ObjectId::new(10.try_into().unwrap()))) };
unsafe { builder.add_fd(&*DUMMY_FD).unwrap() };
assert_eq!(
&builder.msg[..builder.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
1 | ((8 + 4 + 4 + 4 + 4 + 4) << 16),
1,
2,
-1i32 as u32,
WlFixed::from(1i32).0 as u32,
ObjectId::new(10.try_into().unwrap()).get().get()
]
);
assert_eq!(
&builder.fds[..builder.fds_len as usize],
&[DUMMY_FD.as_raw_fd()]
)
}
#[test]
fn overflowing_msg() {
let tmpdir = TempDir::new("test_overflowing_msg").unwrap();
let arr = std::vec![0u8; 4000];
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
let mut builder = MessageBuilder::new();
unsafe { builder.add_header(DUMMY_OBJ, 0, 8 + arr.len() as u16 + 4) };
unsafe { builder.add_array(&arr) };
unsafe { builder.add_header(DUMMY_OBJ, 1, 8) };
builder
.ensure_space(&sender_fd, 2 + 1 + arr.len().div_ceil(4), 0)
.unwrap();
unsafe { builder.add_header(DUMMY_OBJ, 0, 8 + arr.len() as u16 + 4) };
unsafe { builder.add_array(&arr) };
builder.flush(&sender_fd).unwrap();
}
{
let fd = rustix::net::accept(receiver_fd).unwrap();
let mut receiver = Receiver::new();
let end = {
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let expected = &arr;
let array = msg.next_array();
assert_eq!(array.len(), expected.len());
assert_eq!(array, expected);
assert!(msg.next().unwrap().is_ok());
assert_eq!(
&msg.payload.bytes()[msg.payload.cur as usize - 2..msg.payload.cur as usize],
&[DUMMY_OBJ.get().get(), 1 | (8 << 16)]
);
assert!(msg.next().is_none());
msg.payload.cur
};
assert_eq!(receiver.payload.cur, WL_MAX_MESSAGE_SIZE_U32 as u16 - end);
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let expected = &arr;
let array = msg.next_array();
assert_eq!(array.len(), expected.len());
assert_eq!(array, expected);
}
}
#[test]
fn receiving_incomplete_messages() {
let tmpdir = TempDir::new("test_receiving_incomplete_messages").unwrap();
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
let mut receiver = Receiver::new();
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[1, 12 << 16]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
{
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().is_none());
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 2);
}
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[2]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(1).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 2);
assert!(msg.next().is_none());
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 0);
}
#[test]
fn receiving_multiple_messages() {
let tmpdir = TempDir::new("test_receiving_multiple_messages").unwrap();
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[1, 12 << 16, 2, 3, 12 << 16, 4]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
let mut receiver = Receiver::new();
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(1).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 2);
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(3).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 4);
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 0);
}
#[test]
fn receive_invalid_string() {
let tmpdir = TempDir::new("test_receiving_multiple_messages").unwrap();
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[1, 12 << 16, 5]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
let mut receiver = Receiver::new();
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
assert!(matches!(
msg.validate_len([true]),
Err(Error::InvalidArrayOrStringLength)
));
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(1).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 5);
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 0);
}
#[test]
#[should_panic]
fn receive_too_large_message() {
let tmpdir = TempDir::new("test_receiving_multiple_messages").unwrap();
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[1, (WL_MAX_MESSAGE_SIZE as u32 + 1) << 16, 5]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
let mut receiver = Receiver::new();
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
let _ = msg.next();
}
#[test]
#[should_panic]
fn message_larger_than_the_protocol_maximum() {
let mut builder = MessageBuilder::new();
builder
.ensure_space(&DUMMY_FD.as_fd(), WL_MAX_MESSAGE_SIZE_U32 + 1, 0)
.unwrap();
}
#[test]
#[should_panic]
fn more_fds_than_we_can_handle() {
let mut builder = MessageBuilder::new();
builder
.ensure_space(&DUMMY_FD.as_fd(), 12, builder.fds.len() + 1)
.unwrap();
}
}