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,
6};
7
8#[cfg(any(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(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(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, Clone)]
60#[allow(clippy::box_collection)]
61pub enum Argument<Id, Fd> {
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 [`RawFd`].
83    ///
84    /// [`RawFd`]: std::os::fd::RawFd
85    Fd(Fd),
86}
87
88impl<Id, Fd> Argument<Id, Fd> {
89    /// Retrieve the type of a given argument instance
90    pub fn get_type(&self) -> ArgumentType {
91        match *self {
92            Self::Int(_) => ArgumentType::Int,
93            Self::Uint(_) => ArgumentType::Uint,
94            Self::Fixed(_) => ArgumentType::Fixed,
95            Self::Str(_) => ArgumentType::Str(AllowNull::Yes),
96            Self::Object(_) => ArgumentType::Object(AllowNull::Yes),
97            Self::NewId(_) => ArgumentType::NewId,
98            Self::Array(_) => ArgumentType::Array,
99            Self::Fd(_) => ArgumentType::Fd,
100        }
101    }
102
103    fn map_fd<T>(self, f: &mut impl FnMut(Fd) -> T) -> Argument<Id, T> {
104        match self {
105            Self::Int(val) => Argument::Int(val),
106            Self::Uint(val) => Argument::Uint(val),
107            Self::Fixed(val) => Argument::Fixed(val),
108            Self::Str(val) => Argument::Str(val),
109            Self::Object(val) => Argument::Object(val),
110            Self::NewId(val) => Argument::NewId(val),
111            Self::Array(val) => Argument::Array(val),
112            Self::Fd(val) => Argument::Fd(f(val)),
113        }
114    }
115}
116
117impl<Id: PartialEq, Fd: AsRawFd> PartialEq for Argument<Id, Fd> {
118    fn eq(&self, other: &Self) -> bool {
119        match (self, other) {
120            (Self::Int(a), Self::Int(b)) => a == b,
121            (Self::Uint(a), Self::Uint(b)) => a == b,
122            (Self::Fixed(a), Self::Fixed(b)) => a == b,
123            (Self::Str(a), Self::Str(b)) => a == b,
124            (Self::Object(a), Self::Object(b)) => a == b,
125            (Self::NewId(a), Self::NewId(b)) => a == b,
126            (Self::Array(a), Self::Array(b)) => a == b,
127            (Self::Fd(a), Self::Fd(b)) => a.as_raw_fd() == b.as_raw_fd(),
128            _ => false,
129        }
130    }
131}
132
133impl<Id: Eq, Fd: AsRawFd> Eq for Argument<Id, Fd> {}
134
135impl<Id: std::fmt::Display, Fd: AsRawFd> std::fmt::Display for Argument<Id, Fd> {
136    #[cfg_attr(unstable_coverage, coverage(off))]
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        match self {
139            Self::Int(value) => write!(f, "{value}"),
140            Self::Uint(value) => write!(f, "{value}"),
141            Self::Fixed(value) => write!(f, "{:.4}", *value as f64 / 256.0),
142            Self::Str(value) => write!(f, "{value:?}"),
143            Self::Object(value) => write!(f, "{value}"),
144            Self::NewId(value) => write!(f, "{value}"),
145            Self::Array(value) => write!(f, "{value:?}"),
146            Self::Fd(value) => write!(f, "{}", value.as_raw_fd()),
147        }
148    }
149}
150
151/// Description of wayland interface.
152///
153/// An interface describes the possible requests and events that a wayland client and compositor use to
154/// communicate.
155#[derive(Debug)]
156pub struct Interface {
157    /// The name of the interface.
158    pub name: &'static str,
159    /// The maximum supported version of the interface.
160    pub version: u32,
161    /// A list that describes every request this interface supports.
162    pub requests: &'static [MessageDesc],
163    /// A list that describes every event this interface supports.
164    pub events: &'static [MessageDesc],
165    /// A C representation of this interface that may be used to interoperate with libwayland.
166    pub c_interface: Option<&'static CWlInterface>,
167}
168
169/// Wrapper around `wl_interface` used in libwayland to define interfaces
170#[derive(Debug)]
171#[repr(transparent)]
172pub struct CWlInterface(pub(crate) wl_interface);
173
174unsafe impl Sync for CWlInterface {}
175
176impl CWlInterface {
177    /// Construct a `wl_interface` to store in a static
178    #[cfg(any(feature = "client_system", feature = "server_system"))]
179    pub const fn new(
180        name: &'static CStr,
181        version: u32,
182        requests: &'static [CWlMessage],
183        events: &'static [CWlMessage],
184    ) -> Self {
185        Self(wl_interface {
186            name: name.as_ptr(),
187            version: version as _,
188            request_count: requests.len() as _,
189            requests: requests.as_ptr() as _,
190            event_count: events.len() as _,
191            events: events.as_ptr() as _,
192        })
193    }
194
195    /// Construct a `wl_interface` to store in a static
196    #[cfg(not(any(feature = "client_system", feature = "server_system")))]
197    pub const fn new(
198        name: &'static CStr,
199        version: u32,
200        requests: &'static [CWlMessage],
201        events: &'static [CWlMessage],
202    ) -> Self {
203        let _ = (name, version, requests, events);
204        Self(std::marker::PhantomData)
205    }
206}
207
208/// Wrapper around `wl_message` used in libwayland to define messages in interfaces
209#[allow(missing_debug_implementations)]
210#[repr(transparent)]
211pub struct CWlMessage(wl_message);
212
213unsafe impl Sync for CWlMessage {}
214
215impl CWlMessage {
216    /// Construct a `wl_message` to store in a static
217    #[cfg(any(feature = "client_system", feature = "server_system"))]
218    pub const fn new(
219        name: &'static CStr,
220        signature: &'static CStr,
221        // `Option<&wl_interface>` has the same repr as `*const wl_interface`
222        types: &'static [Option<&'static CWlInterface>],
223    ) -> Self {
224        Self(wl_message {
225            name: name.as_ptr(),
226            signature: signature.as_ptr(),
227            types: types.as_ptr() as *const *const wl_interface,
228        })
229    }
230
231    /// Construct a `wl_message` to store in a static
232    #[cfg(not(any(feature = "client_system", feature = "server_system")))]
233    pub const fn new(
234        name: &'static CStr,
235        signature: &'static CStr,
236        types: &'static [Option<&'static CWlInterface>],
237    ) -> Self {
238        let _ = (name, signature, types);
239        Self(std::marker::PhantomData)
240    }
241}
242
243impl std::fmt::Display for Interface {
244    #[cfg_attr(unstable_coverage, coverage(off))]
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246        f.write_str(self.name)
247    }
248}
249
250/// Wire metadata of a given message
251#[derive(Copy, Clone, Debug)]
252pub struct MessageDesc {
253    /// Name of this message
254    pub name: &'static str,
255    /// Signature of the message
256    pub signature: &'static [ArgumentType],
257    /// Minimum required version of the interface
258    pub since: u32,
259    /// Whether this message is a destructor
260    pub is_destructor: bool,
261    /// The child interface created from this message.
262    ///
263    /// In the wayland xml format, this corresponds to the `new_id` type.
264    pub child_interface: Option<&'static Interface>,
265    /// The interfaces passed into this message as arguments.
266    pub arg_interfaces: &'static [&'static Interface],
267}
268
269/// Special interface representing an anonymous object
270pub static ANONYMOUS_INTERFACE: Interface =
271    Interface { name: "<anonymous>", version: 0, requests: &[], events: &[], c_interface: None };
272
273/// Description of the protocol-level information of an object
274#[derive(Copy, Clone, Debug)]
275pub struct ObjectInfo {
276    /// The protocol ID
277    pub id: u32,
278    /// The interface
279    pub interface: &'static Interface,
280    /// The version
281    pub version: u32,
282}
283
284/// A protocol error
285///
286/// This kind of error is generated by the server if your client didn't respect
287/// the protocol, after which the server will kill your connection.
288#[derive(Clone, Debug)]
289pub struct ProtocolError {
290    /// The error code associated with the error
291    ///
292    /// It should be interpreted as an instance of the `Error` enum of the
293    /// associated interface.
294    pub code: u32,
295    /// The id of the object that caused the error
296    pub object_id: u32,
297    /// The interface of the object that caused the error
298    pub object_interface: String,
299    /// The message sent by the server describing the error
300    pub message: String,
301}
302
303/// Number of arguments that are stocked inline in a `Message` before allocating
304///
305/// This is a ad-hoc number trying to reach a good balance between avoiding too many allocations
306/// and keeping the stack size of `Message` small.
307// Note: Keep in sync with `wayland_scanner::common::gen_write_body`.
308pub const INLINE_ARGS: usize = 4;
309
310/// Represents a message that has been sent from some object.
311#[derive(Clone, Debug)]
312pub struct Message<Id, Fd> {
313    /// The id of the object that sent the message.
314    pub sender_id: Id,
315    /// The opcode of the message.
316    pub opcode: u16,
317    /// The arguments of the message.
318    pub args: smallvec::SmallVec<[Argument<Id, Fd>; INLINE_ARGS]>,
319}
320
321impl<Id, Fd> Message<Id, Fd> {
322    /// Map some closure on all Fd contained in this message, to change the Fd generic parameter.
323    pub fn map_fd<T>(self, mut f: impl FnMut(Fd) -> T) -> Message<Id, T> {
324        Message {
325            sender_id: self.sender_id,
326            opcode: self.opcode,
327            args: self.args.into_iter().map(move |arg| arg.map_fd(&mut f)).collect(),
328        }
329    }
330}
331
332impl<Id: PartialEq, Fd: AsRawFd> PartialEq for Message<Id, Fd> {
333    fn eq(&self, other: &Self) -> bool {
334        self.sender_id == other.sender_id && self.opcode == other.opcode && self.args == other.args
335    }
336}
337
338impl<Id: Eq, Fd: AsRawFd> Eq for Message<Id, Fd> {}
339
340impl std::error::Error for ProtocolError {}
341
342impl std::fmt::Display for ProtocolError {
343    #[cfg_attr(unstable_coverage, coverage(off))]
344    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
345        if self.message.is_empty() {
346            // On `sys` backend, we don't have contents of the message
347            write!(
348                f,
349                "Protocol error {} on object {}@{}",
350                self.code, self.object_interface, self.object_id
351            )
352        } else {
353            write!(
354                f,
355                "Protocol error {} on object {}@{}: {}",
356                self.code, self.object_interface, self.object_id, self.message
357            )
358        }
359    }
360}
361
362/// Returns true if the two interfaces are the same.
363#[inline]
364pub fn same_interface(a: &'static Interface, b: &'static Interface) -> bool {
365    std::ptr::eq(a, b) || a.name == b.name
366}
367
368pub(crate) fn check_for_signature<Id, Fd>(
369    signature: &[ArgumentType],
370    args: &[Argument<Id, Fd>],
371) -> bool {
372    if signature.len() != args.len() {
373        return false;
374    }
375    for (typ, arg) in signature.iter().copied().zip(args.iter()) {
376        if !arg.get_type().same_type(typ) {
377            return false;
378        }
379    }
380    true
381}
382
383#[inline]
384#[allow(dead_code)]
385pub(crate) fn same_interface_or_anonymous(a: &'static Interface, b: &'static Interface) -> bool {
386    same_interface(a, b) || same_interface(a, &ANONYMOUS_INTERFACE)
387}