Skip to main content

wayland_backend/
protocol.rs

1//! Types and utilities for manipulating the Wayland protocol
2
3use std::{
4    ffi::{CStr, CString},
5    os::unix::io::{AsRawFd, BorrowedFd, OwnedFd},
6};
7
8#[cfg(any(test, feature = "client_system", feature = "server_system"))]
9use wayland_sys::common::{wl_interface, wl_message};
10
11// Zero-size placeholder with same auto traits, for consistency
12#[cfg(not(any(test, feature = "client_system", feature = "server_system")))]
13#[allow(non_camel_case_types)]
14type wl_interface = std::marker::PhantomData<*const ()>;
15#[allow(non_camel_case_types)]
16#[cfg(not(any(test, feature = "client_system", feature = "server_system")))]
17type wl_message = std::marker::PhantomData<*const ()>;
18
19/// Describes whether an argument may have a null value.
20#[derive(Clone, Copy, PartialEq, Eq, Debug)]
21pub enum AllowNull {
22    /// Null values are allowed.
23    Yes,
24    /// Null values are forbidden.
25    No,
26}
27
28/// Enum of possible argument types as recognized by the wire
29#[derive(Copy, Clone, PartialEq, Eq, Debug)]
30pub enum ArgumentType {
31    /// An integer argument. Represented by a [`i32`].
32    Int,
33    /// An unsigned integer argument. Represented by a [`u32`].
34    Uint,
35    /// A signed fixed point number with 1/256 precision
36    Fixed,
37    /// A string. This is represented as a [`CString`] in a message.
38    Str(AllowNull),
39    /// Id of a wayland object
40    Object(AllowNull),
41    /// Id of a newly created wayland object
42    NewId,
43    /// `Vec<u8>`
44    Array,
45    /// A file descriptor argument. Represented by a [`RawFd`].
46    ///
47    /// [`RawFd`]: std::os::fd::RawFd
48    Fd,
49}
50
51impl ArgumentType {
52    /// Returns true if the type of the argument is the same.
53    pub fn same_type(self, other: Self) -> bool {
54        std::mem::discriminant(&self) == std::mem::discriminant(&other)
55    }
56}
57
58/// Enum of possible argument of the protocol
59#[derive(Debug)]
60#[allow(clippy::box_collection)]
61pub enum OwnedArgument<Id> {
62    /// An integer argument. Represented by a [`i32`].
63    Int(i32),
64    /// An unsigned integer argument. Represented by a [`u32`].
65    Uint(u32),
66    /// A signed fixed point number with 1/256 precision
67    Fixed(i32),
68    /// CString
69    ///
70    /// The value is boxed to reduce the stack size of Argument. The performance
71    /// impact is negligible as `string` arguments are pretty rare in the protocol.
72    Str(Option<Box<CString>>),
73    /// Id of a wayland object
74    Object(Id),
75    /// Id of a newly created wayland object
76    NewId(Id),
77    /// `Vec<u8>`
78    ///
79    /// The value is boxed to reduce the stack size of Argument. The performance
80    /// impact is negligible as `array` arguments are pretty rare in the protocol.
81    Array(Box<Vec<u8>>),
82    /// A file descriptor argument. Represented by a [`OwnedFd`].
83    Fd(OwnedFd),
84}
85
86impl<Id: PartialEq> PartialEq for OwnedArgument<Id> {
87    fn eq(&self, other: &Self) -> bool {
88        match (self, other) {
89            (Self::Int(a), Self::Int(b)) => a == b,
90            (Self::Uint(a), Self::Uint(b)) => a == b,
91            (Self::Fixed(a), Self::Fixed(b)) => a == b,
92            (Self::Str(a), Self::Str(b)) => a == b,
93            (Self::Object(a), Self::Object(b)) => a == b,
94            (Self::NewId(a), Self::NewId(b)) => a == b,
95            (Self::Array(a), Self::Array(b)) => a == b,
96            // Owned file descriptors must be different
97            (Self::Fd(_), Self::Fd(_)) => false,
98            _ => false,
99        }
100    }
101}
102
103impl<Id: Clone> OwnedArgument<Id> {
104    #[cfg(test)]
105    pub(crate) fn as_argument(&self) -> Argument<'_, Id> {
106        use std::os::fd::AsFd;
107        match self {
108            Self::Int(val) => Argument::Int(*val),
109            Self::Uint(val) => Argument::Uint(*val),
110            Self::Fixed(val) => Argument::Fixed(*val),
111            Self::Str(val) => Argument::Str(val.clone()),
112            Self::Object(val) => Argument::Object(val),
113            Self::NewId(val) => Argument::NewId(val),
114            Self::Array(val) => Argument::Array(Box::new(val)),
115            Self::Fd(val) => Argument::Fd(val.as_fd()),
116        }
117    }
118}
119
120impl<Id: Eq> Eq for OwnedArgument<Id> {}
121
122/// Enum of possible argument of the protocol
123#[derive(Debug, Clone)]
124#[allow(clippy::box_collection)]
125pub enum Argument<'a, Id> {
126    /// An integer argument. Represented by a [`i32`].
127    Int(i32),
128    /// An unsigned integer argument. Represented by a [`u32`].
129    Uint(u32),
130    /// A signed fixed point number with 1/256 precision
131    Fixed(i32),
132    /// CString
133    ///
134    /// The value is boxed to reduce the stack size of Argument. The performance
135    /// impact is negligible as `string` arguments are pretty rare in the protocol.
136    Str(Option<Box<CString>>),
137    /// Id of a wayland object
138    Object(&'a Id),
139    /// Id of a newly created wayland object
140    NewId(&'a Id),
141    /// `Vec<u8>`
142    ///
143    /// The value is boxed to reduce the stack size of Argument. The performance
144    /// impact is negligible as `array` arguments are pretty rare in the protocol.
145    Array(Box<&'a [u8]>),
146    /// A file descriptor argument. Represented by a [`BorrowedFd`].
147    Fd(BorrowedFd<'a>),
148}
149
150impl<'a, Id> Argument<'a, Id> {
151    /// Retrieve the type of a given argument instance
152    pub fn get_type(&self) -> ArgumentType {
153        match *self {
154            Self::Int(_) => ArgumentType::Int,
155            Self::Uint(_) => ArgumentType::Uint,
156            Self::Fixed(_) => ArgumentType::Fixed,
157            Self::Str(_) => ArgumentType::Str(AllowNull::Yes),
158            Self::Object(_) => ArgumentType::Object(AllowNull::Yes),
159            Self::NewId(_) => ArgumentType::NewId,
160            Self::Array(_) => ArgumentType::Array,
161            Self::Fd(_) => ArgumentType::Fd,
162        }
163    }
164}
165
166impl<'a, Id: PartialEq> PartialEq for Argument<'a, Id> {
167    fn eq(&self, other: &Self) -> bool {
168        match (self, other) {
169            (Self::Int(a), Self::Int(b)) => a == b,
170            (Self::Uint(a), Self::Uint(b)) => a == b,
171            (Self::Fixed(a), Self::Fixed(b)) => a == b,
172            (Self::Str(a), Self::Str(b)) => a == b,
173            (Self::Object(a), Self::Object(b)) => a == b,
174            (Self::NewId(a), Self::NewId(b)) => a == b,
175            (Self::Array(a), Self::Array(b)) => a == b,
176            (Self::Fd(a), Self::Fd(b)) => a.as_raw_fd() == b.as_raw_fd(),
177            _ => false,
178        }
179    }
180}
181
182impl<'a, Id: Eq> Eq for Argument<'a, Id> {}
183
184impl<'a, Id: std::fmt::Display> std::fmt::Display for Argument<'a, Id> {
185    #[cfg_attr(unstable_coverage, coverage(off))]
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        match self {
188            Self::Int(value) => write!(f, "{value}"),
189            Self::Uint(value) => write!(f, "{value}"),
190            Self::Fixed(value) => write!(f, "{:.4}", *value as f64 / 256.0),
191            Self::Str(value) => write!(f, "{value:?}"),
192            Self::Object(value) => write!(f, "{value}"),
193            Self::NewId(value) => write!(f, "{value}"),
194            Self::Array(value) => write!(f, "{value:?}"),
195            Self::Fd(value) => write!(f, "{}", value.as_raw_fd()),
196        }
197    }
198}
199
200impl<Id: std::fmt::Display> std::fmt::Display for OwnedArgument<Id> {
201    #[cfg_attr(unstable_coverage, coverage(off))]
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        match self {
204            Self::Int(value) => write!(f, "{value}"),
205            Self::Uint(value) => write!(f, "{value}"),
206            Self::Fixed(value) => write!(f, "{:.4}", *value as f64 / 256.0),
207            Self::Str(value) => write!(f, "{value:?}"),
208            Self::Object(value) => write!(f, "{value}"),
209            Self::NewId(value) => write!(f, "{value}"),
210            Self::Array(value) => write!(f, "{value:?}"),
211            Self::Fd(value) => write!(f, "{}", value.as_raw_fd()),
212        }
213    }
214}
215
216/// Description of wayland interface.
217///
218/// An interface describes the possible requests and events that a wayland client and compositor use to
219/// communicate.
220#[derive(Debug)]
221pub struct Interface {
222    /// The name of the interface.
223    pub name: &'static str,
224    /// The maximum supported version of the interface.
225    pub version: u32,
226    /// A list that describes every request this interface supports.
227    pub requests: &'static [MessageDesc],
228    /// A list that describes every event this interface supports.
229    pub events: &'static [MessageDesc],
230    /// A C representation of this interface that may be used to interoperate with libwayland.
231    pub c_interface: Option<&'static CWlInterface>,
232}
233
234/// Wrapper around `wl_interface` used in libwayland to define interfaces
235#[derive(Debug)]
236#[repr(transparent)]
237pub struct CWlInterface(pub(crate) wl_interface);
238
239unsafe impl Sync for CWlInterface {}
240
241impl CWlInterface {
242    /// Construct a `wl_interface` to store in a static
243    #[cfg(any(test, feature = "client_system", feature = "server_system"))]
244    pub const fn new(
245        name: &'static CStr,
246        version: u32,
247        requests: &'static [CWlMessage],
248        events: &'static [CWlMessage],
249    ) -> Self {
250        Self(wl_interface {
251            name: name.as_ptr(),
252            version: version as _,
253            request_count: requests.len() as _,
254            requests: requests.as_ptr() as _,
255            event_count: events.len() as _,
256            events: events.as_ptr() as _,
257        })
258    }
259
260    /// Construct a `wl_interface` to store in a static
261    #[cfg(not(any(test, feature = "client_system", feature = "server_system")))]
262    pub const fn new(
263        name: &'static CStr,
264        version: u32,
265        requests: &'static [CWlMessage],
266        events: &'static [CWlMessage],
267    ) -> Self {
268        let _ = (name, version, requests, events);
269        Self(std::marker::PhantomData)
270    }
271}
272
273/// Wrapper around `wl_message` used in libwayland to define messages in interfaces
274#[allow(missing_debug_implementations)]
275#[repr(transparent)]
276pub struct CWlMessage(wl_message);
277
278unsafe impl Sync for CWlMessage {}
279
280impl CWlMessage {
281    /// Construct a `wl_message` to store in a static
282    #[cfg(any(test, feature = "client_system", feature = "server_system"))]
283    pub const fn new(
284        name: &'static CStr,
285        signature: &'static CStr,
286        // `Option<&wl_interface>` has the same repr as `*const wl_interface`
287        types: &'static [Option<&'static CWlInterface>],
288    ) -> Self {
289        Self(wl_message {
290            name: name.as_ptr(),
291            signature: signature.as_ptr(),
292            types: types.as_ptr() as *const *const wl_interface,
293        })
294    }
295
296    /// Construct a `wl_message` to store in a static
297    #[cfg(not(any(test, feature = "client_system", feature = "server_system")))]
298    pub const fn new(
299        name: &'static CStr,
300        signature: &'static CStr,
301        types: &'static [Option<&'static CWlInterface>],
302    ) -> Self {
303        let _ = (name, signature, types);
304        Self(std::marker::PhantomData)
305    }
306}
307
308impl std::fmt::Display for Interface {
309    #[cfg_attr(unstable_coverage, coverage(off))]
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        f.write_str(self.name)
312    }
313}
314
315/// Wire metadata of a given message
316#[derive(Copy, Clone, Debug)]
317pub struct MessageDesc {
318    /// Name of this message
319    pub name: &'static str,
320    /// Signature of the message
321    pub signature: &'static [ArgumentType],
322    /// Minimum required version of the interface
323    pub since: u32,
324    /// Whether this message is a destructor
325    pub is_destructor: bool,
326    /// The child interface created from this message.
327    ///
328    /// In the wayland xml format, this corresponds to the `new_id` type.
329    pub child_interface: Option<&'static Interface>,
330    /// The interfaces passed into this message as arguments.
331    pub arg_interfaces: &'static [&'static Interface],
332}
333
334/// Special interface representing an anonymous object
335pub static ANONYMOUS_INTERFACE: Interface =
336    Interface { name: "<anonymous>", version: 0, requests: &[], events: &[], c_interface: None };
337
338/// Description of the protocol-level information of an object
339#[derive(Copy, Clone, Debug)]
340pub struct ObjectInfo {
341    /// The protocol ID
342    pub id: u32,
343    /// The interface
344    pub interface: &'static Interface,
345    /// The version
346    pub version: u32,
347}
348
349/// A protocol error
350///
351/// This kind of error is generated by the server if your client didn't respect
352/// the protocol, after which the server will kill your connection.
353#[derive(Clone, Debug)]
354pub struct ProtocolError {
355    /// The error code associated with the error
356    ///
357    /// It should be interpreted as an instance of the `Error` enum of the
358    /// associated interface.
359    pub code: u32,
360    /// The id of the object that caused the error
361    pub object_id: u32,
362    /// The interface of the object that caused the error
363    pub object_interface: String,
364    /// The message sent by the server describing the error
365    pub message: String,
366}
367
368/// Number of arguments that are stocked inline in a `Message` before allocating
369///
370/// This is a ad-hoc number trying to reach a good balance between avoiding too many allocations
371/// and keeping the stack size of `Message` small.
372// Note: Keep in sync with `wayland_scanner::common::gen_write_body`.
373pub const INLINE_ARGS: usize = 4;
374
375/// Represents a message that has been sent from some object.
376#[derive(Debug, PartialEq, Eq)]
377pub struct OwnedMessage<Id> {
378    /// The id of the object that sent the message.
379    pub sender_id: Id,
380    /// The opcode of the message.
381    pub opcode: u16,
382    /// The arguments of the message.
383    pub args: smallvec::SmallVec<[OwnedArgument<Id>; INLINE_ARGS]>,
384}
385
386/*
387impl<Id: Clone> OwnedMessage<Id> {
388    #[cfg(test)]
389    pub(crate) fn as_message(&self) -> Message<'_, Id> {
390        Message {
391            sender_id: self.sender_id.clone(),
392            opcode: self.opcode,
393            args: self.args.iter().map(|arg| arg.as_argument()).collect(),
394        }
395    }
396}
397*/
398
399/// Represents a message that has been sent from some object.
400#[derive(Clone, Debug, PartialEq, Eq)]
401pub struct Message<'a, Id> {
402    /// The id of the object that sent the message.
403    pub sender_id: &'a Id,
404    /// The opcode of the message.
405    pub opcode: u16,
406    /// The arguments of the message.
407    pub args: smallvec::SmallVec<[Argument<'a, Id>; INLINE_ARGS]>,
408}
409
410impl std::error::Error for ProtocolError {}
411
412impl std::fmt::Display for ProtocolError {
413    #[cfg_attr(unstable_coverage, coverage(off))]
414    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
415        if self.message.is_empty() {
416            // On `sys` backend, we don't have contents of the message
417            write!(
418                f,
419                "Protocol error {} on object {}@{}",
420                self.code, self.object_interface, self.object_id
421            )
422        } else {
423            write!(
424                f,
425                "Protocol error {} on object {}@{}: {}",
426                self.code, self.object_interface, self.object_id, self.message
427            )
428        }
429    }
430}
431
432/// Returns true if the two interfaces are the same.
433#[inline]
434pub fn same_interface(a: &'static Interface, b: &'static Interface) -> bool {
435    std::ptr::eq(a, b) || a.name == b.name
436}
437
438pub(crate) fn check_for_signature<Id>(signature: &[ArgumentType], args: &[Argument<Id>]) -> bool {
439    if signature.len() != args.len() {
440        return false;
441    }
442    for (typ, arg) in signature.iter().copied().zip(args.iter()) {
443        if !arg.get_type().same_type(typ) {
444            return false;
445        }
446    }
447    true
448}
449
450#[inline]
451#[allow(dead_code)]
452pub(crate) fn same_interface_or_anonymous(a: &'static Interface, b: &'static Interface) -> bool {
453    same_interface(a, b) || same_interface(a, &ANONYMOUS_INTERFACE)
454}
455
456#[cfg(test)]
457mod test {
458    use std::mem;
459
460    use super::*;
461
462    #[test]
463    fn test_argument_size() {
464        // Shouldn't be larger than a pointer, plus a discriminant of same size
465        assert!(mem::size_of::<Argument<'_, usize>>() <= 2 * mem::size_of::<usize>());
466        assert!(mem::size_of::<OwnedArgument<usize>>() <= 2 * mem::size_of::<usize>());
467    }
468}