waybackend 0.10.1

A simple, low-level wayland client implementation
Documentation
//! This module contains the definitions of all wayland types that can go through the wire.

pub mod german_string;

use core::{mem::MaybeUninit, num::NonZeroU32};

use crate::wire::Error;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ObjectId(NonZeroU32);

#[derive(Debug, Clone)]
pub struct WlSlice<'a>(pub(crate) &'a [u8]);

#[derive(Debug, Clone)]
pub struct WlStr<'a>(pub(crate) &'a str);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct WlFixed(pub(crate) i32);

#[derive(Debug, Clone)]
pub struct NewId<'a> {
    pub(crate) id: ObjectId,
    pub(crate) interface: &'a str,
    pub(crate) version: u32,
}

impl ObjectId {
    #[inline]
    #[must_use]
    pub const fn get(self) -> NonZeroU32 {
        self.0
    }

    #[inline]
    #[must_use]
    pub(crate) const fn new(value: NonZeroU32) -> Self {
        Self(value)
    }

    #[inline]
    pub(crate) const fn try_new(value: u32) -> Result<Self, Error> {
        match NonZeroU32::new(value) {
            Some(x) => Ok(Self(x)),
            None => Err(Error::NullObjectId),
        }
    }

    #[inline]
    #[must_use]
    pub const fn null() -> Option<Self> {
        None
    }

    #[inline]
    #[must_use]
    pub const fn created_by_server(self) -> bool {
        self.0.get() >= 0xFF000000
    }
}

impl core::fmt::Display for ObjectId {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(rustix::path::DecInt::new(self.get().get()).as_str())
    }
}

impl WlSlice<'_> {
    #[inline]
    #[must_use]
    /// returns how many bytes we have written (including the prepended length)
    pub fn encode(&self, buf: &mut [u32]) -> usize {
        let len = self.0.len().next_multiple_of(4);
        assert!(
            len + 1 < buf.len() * 4,
            "slice is too big ({}) to be encoded in the wayland wire format (max: {})",
            len + 1,
            buf.len() * 4
        );
        buf[0] = self.0.len() as u32;
        unsafe {
            // dst is the next free position in the buffer
            let dst = buf.as_mut_ptr().add(1) as *mut u8;

            // copy all the bytes
            // SAFETY: we've ensured the buf's pointer has the necessary size above
            core::ptr::copy_nonoverlapping(self.0.as_ptr(), dst, self.0.len());

            // 0 initialize the padding
            for i in self.0.len()..len {
                dst.add(i).write(0);
            }
        }
        4 + len
    }
}

impl<'a, 'b> From<&'b [u8]> for WlSlice<'a>
where
    'b: 'a,
{
    #[inline]
    fn from(bytes: &'b [u8]) -> Self {
        Self(bytes)
    }
}

impl WlStr<'_> {
    #[inline]
    #[must_use]
    /// returns how many bytes we have written (including the prepended length)
    pub fn encode(&self, buf: &mut [u32]) -> usize {
        let bytes = self.0.as_bytes();
        // add one for the null terminator
        let len = (bytes.len() + 1).next_multiple_of(4);
        assert!(
            len + 1 < buf.len() * 4,
            "string is too big ({}) to be encoded in the wayland wire format (max: {})",
            len + 1,
            buf.len() * 4
        );
        buf[0] = self.0.len() as u32 + 1;
        unsafe {
            // dst is the next free position in the buffer
            let dst = buf.as_mut_ptr().add(1) as *mut u8;

            // copy all the bytes
            // SAFETY: we've ensured the buf's pointer has the necessary size above
            core::ptr::copy_nonoverlapping(bytes.as_ptr(), dst, bytes.len());

            // 0 initialize the padding and the null terminator
            for i in bytes.len()..len {
                dst.add(i).write(0);
            }
        }
        4 + len
    }
}

impl<'a, 'b> From<&'b str> for WlStr<'a>
where
    'b: 'a,
{
    #[inline]
    fn from(s: &'b str) -> Self {
        Self(s)
    }
}

impl From<i32> for WlFixed {
    #[inline]
    fn from(value: i32) -> Self {
        Self(value * 256)
    }
}

impl From<u32> for WlFixed {
    #[inline]
    fn from(value: u32) -> Self {
        Self(value as i32 * 256)
    }
}

impl From<WlFixed> for i32 {
    #[inline]
    fn from(val: WlFixed) -> Self {
        val.0 / 256
    }
}

impl From<f64> for WlFixed {
    #[inline]
    fn from(value: f64) -> Self {
        let d = value + (3i64 << (51 - 8)) as f64;
        Self(d.to_bits() as i32)
    }
}

impl From<WlFixed> for f64 {
    #[inline]
    fn from(val: WlFixed) -> Self {
        let i = ((1023i64 + 44i64) << 52) + (1i64 << 51) + val.0 as i64;
        let d = f64::from_bits(i as u64);
        d - (3i64 << 43) as f64
    }
}

impl NewId<'_> {
    #[inline]
    #[must_use]
    pub fn id(&self) -> ObjectId {
        self.id
    }

    #[inline]
    #[must_use]
    pub fn interface(&self) -> &str {
        self.interface
    }

    #[inline]
    #[must_use]
    pub fn version(&self) -> u32 {
        self.version
    }
}

pub(crate) const fn u32_slice_to_u8(src: &[u32]) -> &[u8] {
    let len = src.len() << 2;
    unsafe { core::slice::from_raw_parts(src.as_ptr().cast(), len) }
}

pub(crate) const fn u32_slice_to_u8_mut(src: &mut [u32]) -> &mut [u8] {
    let len = src.len() << 2;
    unsafe { core::slice::from_raw_parts_mut(src.as_mut_ptr().cast(), len) }
}

pub(crate) const fn i32_slice_to_u8_mut(src: &mut [MaybeUninit<i32>]) -> &mut [MaybeUninit<u8>] {
    let len = src.len() << 2;
    unsafe { core::slice::from_raw_parts_mut(src.as_mut_ptr().cast(), len) }
}

#[cfg(test)]
mod tests {
    extern crate std;
    use super::*;

    #[test]
    fn fixed_creation() {
        assert_eq!(WlFixed::from(-1), WlFixed::from(0xFFFFFFFFu32));
    }

    #[test]
    fn slice_encoding() {
        let mut buf = std::vec![0; 3];

        let arr = [1u8, 2, 3, 4, 5, 6, 7];
        let len = WlSlice::from(arr.as_ref()).encode(&mut buf) / 4;

        #[cfg(target_endian = "little")]
        let expected = [7, 0x04030201u32, 0x00070605];

        #[cfg(target_endian = "big")]
        let expected = [8, 0x01020304u32, 0x05060700];

        assert_eq!(buf[..len], expected);

        let arr = [1u8, 2, 3, 4];
        let len = WlSlice::from(arr.as_ref()).encode(&mut buf) / 4;

        #[cfg(target_endian = "little")]
        let expected = [4, 0x04030201u32];

        #[cfg(target_endian = "big")]
        let expected = [4, 0x01020304u32];

        assert_eq!(buf[..len], expected);
    }

    #[test]
    fn str_encoding() {
        let mut buf = std::vec![0; 4];

        let len = WlStr::from("hello world").encode(&mut buf) / 4;

        #[cfg(target_endian = "little")]
        let expected = [12, 0x6C6C6568u32, 0x6F77206F, 0x00646C72];

        #[cfg(target_endian = "big")]
        let expected = [12, 0x06865C6Cu32, 0x6F20776F, 0x726C6400];

        assert_eq!(buf[..len], expected);

        let len = WlStr::from("hell").encode(&mut buf) / 4;
        #[cfg(target_endian = "little")]
        let expected = [5, 0x6C6C6568u32, 0];

        #[cfg(target_endian = "big")]
        let expected = [8, 0x06865C6Cu32, 0];

        assert_eq!(buf[..len], expected);
    }

    #[test]
    #[should_panic]
    fn slice_encoding_should_panic() {
        let mut buf = std::vec![0; 2];

        let arr = [1u8, 2, 3, 4, 5, 6, 7];
        let _ = WlSlice::from(arr.as_ref()).encode(&mut buf);
    }

    #[test]
    #[should_panic]
    fn str_encoding_should_panic() {
        let mut buf = std::vec![0; 3];

        let _ = WlStr::from("hello world").encode(&mut buf);
    }
}