1 | //! Types and utilities for manipulating the Wayland protocol |
2 | |
3 | use std::{ffi::CString, os::unix::prelude::AsRawFd}; |
4 | |
5 | pub use wayland_sys::common::{wl_argument, wl_interface, wl_message}; |
6 | |
7 | /// Describes whether an argument may have a null value. |
8 | #[derive (Clone, Copy, PartialEq, Eq, Debug)] |
9 | pub enum AllowNull { |
10 | /// Null values are allowed. |
11 | Yes, |
12 | /// Null values are forbidden. |
13 | No, |
14 | } |
15 | |
16 | /// Enum of possible argument types as recognized by the wire |
17 | #[derive (Copy, Clone, PartialEq, Eq, Debug)] |
18 | pub enum ArgumentType { |
19 | /// An integer argument. Represented by a [`i32`]. |
20 | Int, |
21 | /// An unsigned integer argument. Represented by a [`u32`]. |
22 | Uint, |
23 | /// A signed fixed point number with 1/256 precision |
24 | Fixed, |
25 | /// A string. This is represented as a [`CString`] in a message. |
26 | Str(AllowNull), |
27 | /// Id of a wayland object |
28 | Object(AllowNull), |
29 | /// Id of a newly created wayland object |
30 | NewId, |
31 | /// `Vec<u8>` |
32 | Array, |
33 | /// A file descriptor argument. Represented by a [`RawFd`]. |
34 | /// |
35 | /// [`RawFd`]: std::os::fd::RawFd |
36 | Fd, |
37 | } |
38 | |
39 | impl ArgumentType { |
40 | /// Returns true if the type of the argument is the same. |
41 | pub fn same_type(self, other: Self) -> bool { |
42 | std::mem::discriminant(&self) == std::mem::discriminant(&other) |
43 | } |
44 | } |
45 | |
46 | /// Enum of possible argument of the protocol |
47 | #[derive (Debug, Clone)] |
48 | #[allow (clippy::box_collection)] |
49 | pub enum Argument<Id, Fd> { |
50 | /// An integer argument. Represented by a [`i32`]. |
51 | Int(i32), |
52 | /// An unsigned integer argument. Represented by a [`u32`]. |
53 | Uint(u32), |
54 | /// A signed fixed point number with 1/256 precision |
55 | Fixed(i32), |
56 | /// CString |
57 | /// |
58 | /// The value is boxed to reduce the stack size of Argument. The performance |
59 | /// impact is negligible as `string` arguments are pretty rare in the protocol. |
60 | Str(Option<Box<CString>>), |
61 | /// Id of a wayland object |
62 | Object(Id), |
63 | /// Id of a newly created wayland object |
64 | NewId(Id), |
65 | /// `Vec<u8>` |
66 | /// |
67 | /// The value is boxed to reduce the stack size of Argument. The performance |
68 | /// impact is negligible as `array` arguments are pretty rare in the protocol. |
69 | Array(Box<Vec<u8>>), |
70 | /// A file descriptor argument. Represented by a [`RawFd`]. |
71 | /// |
72 | /// [`RawFd`]: std::os::fd::RawFd |
73 | Fd(Fd), |
74 | } |
75 | |
76 | impl<Id, Fd> Argument<Id, Fd> { |
77 | /// Retrieve the type of a given argument instance |
78 | pub fn get_type(&self) -> ArgumentType { |
79 | match *self { |
80 | Self::Int(_) => ArgumentType::Int, |
81 | Self::Uint(_) => ArgumentType::Uint, |
82 | Self::Fixed(_) => ArgumentType::Fixed, |
83 | Self::Str(_) => ArgumentType::Str(AllowNull::Yes), |
84 | Self::Object(_) => ArgumentType::Object(AllowNull::Yes), |
85 | Self::NewId(_) => ArgumentType::NewId, |
86 | Self::Array(_) => ArgumentType::Array, |
87 | Self::Fd(_) => ArgumentType::Fd, |
88 | } |
89 | } |
90 | |
91 | fn map_fd<T>(self, f: &mut impl FnMut(Fd) -> T) -> Argument<Id, T> { |
92 | match self { |
93 | Self::Int(val) => Argument::Int(val), |
94 | Self::Uint(val) => Argument::Uint(val), |
95 | Self::Fixed(val) => Argument::Fixed(val), |
96 | Self::Str(val) => Argument::Str(val), |
97 | Self::Object(val) => Argument::Object(val), |
98 | Self::NewId(val) => Argument::NewId(val), |
99 | Self::Array(val) => Argument::Array(val), |
100 | Self::Fd(val) => Argument::Fd(f(val)), |
101 | } |
102 | } |
103 | } |
104 | |
105 | impl<Id: PartialEq, Fd: AsRawFd> PartialEq for Argument<Id, Fd> { |
106 | fn eq(&self, other: &Self) -> bool { |
107 | match (self, other) { |
108 | (Self::Int(a: &i32), Self::Int(b: &i32)) => a == b, |
109 | (Self::Uint(a: &u32), Self::Uint(b: &u32)) => a == b, |
110 | (Self::Fixed(a: &i32), Self::Fixed(b: &i32)) => a == b, |
111 | (Self::Str(a: &Option>), Self::Str(b: &Option>)) => a == b, |
112 | (Self::Object(a: &Id), Self::Object(b: &Id)) => a == b, |
113 | (Self::NewId(a: &Id), Self::NewId(b: &Id)) => a == b, |
114 | (Self::Array(a: &Box>), Self::Array(b: &Box>)) => a == b, |
115 | (Self::Fd(a: &Fd), Self::Fd(b: &Fd)) => a.as_raw_fd() == b.as_raw_fd(), |
116 | _ => false, |
117 | } |
118 | } |
119 | } |
120 | |
121 | impl<Id: Eq, Fd: AsRawFd> Eq for Argument<Id, Fd> {} |
122 | |
123 | impl<Id: std::fmt::Display, Fd: AsRawFd> std::fmt::Display for Argument<Id, Fd> { |
124 | #[cfg_attr (coverage, coverage(off))] |
125 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
126 | match self { |
127 | Self::Int(value: &i32) => write!(f, " {}" , value), |
128 | Self::Uint(value: &u32) => write!(f, " {}" , value), |
129 | Self::Fixed(value: &i32) => write!(f, " {}" , value), |
130 | Self::Str(value: &Option>) => write!(f, " {:?}" , value), |
131 | Self::Object(value: &Id) => write!(f, " {}" , value), |
132 | Self::NewId(value: &Id) => write!(f, " {}" , value), |
133 | Self::Array(value: &Box>) => write!(f, " {:?}" , value), |
134 | Self::Fd(value: &Fd) => write!(f, " {}" , value.as_raw_fd()), |
135 | } |
136 | } |
137 | } |
138 | |
139 | /// Description of wayland interface. |
140 | /// |
141 | /// An interface describes the possible requests and events that a wayland client and compositor use to |
142 | /// communicate. |
143 | #[derive (Debug)] |
144 | pub struct Interface { |
145 | /// The name of the interface. |
146 | pub name: &'static str, |
147 | /// The maximum supported version of the interface. |
148 | pub version: u32, |
149 | /// A list that describes every request this interface supports. |
150 | pub requests: &'static [MessageDesc], |
151 | /// A list that describes every event this interface supports. |
152 | pub events: &'static [MessageDesc], |
153 | /// A C representation of this interface that may be used to interoperate with libwayland. |
154 | pub c_ptr: Option<&'static wayland_sys::common::wl_interface>, |
155 | } |
156 | |
157 | impl std::fmt::Display for Interface { |
158 | #[cfg_attr (coverage, coverage(off))] |
159 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
160 | f.write_str(self.name) |
161 | } |
162 | } |
163 | |
164 | /// Wire metadata of a given message |
165 | #[derive (Copy, Clone, Debug)] |
166 | pub struct MessageDesc { |
167 | /// Name of this message |
168 | pub name: &'static str, |
169 | /// Signature of the message |
170 | pub signature: &'static [ArgumentType], |
171 | /// Minimum required version of the interface |
172 | pub since: u32, |
173 | /// Whether this message is a destructor |
174 | pub is_destructor: bool, |
175 | /// The child interface created from this message. |
176 | /// |
177 | /// In the wayland xml format, this corresponds to the `new_id` type. |
178 | pub child_interface: Option<&'static Interface>, |
179 | /// The interfaces passed into this message as arguments. |
180 | pub arg_interfaces: &'static [&'static Interface], |
181 | } |
182 | |
183 | /// Special interface representing an anonymous object |
184 | pub static ANONYMOUS_INTERFACE: Interface = |
185 | Interface { name: "<anonymous>" , version: 0, requests: &[], events: &[], c_ptr: None }; |
186 | |
187 | /// Description of the protocol-level information of an object |
188 | #[derive (Copy, Clone, Debug)] |
189 | pub struct ObjectInfo { |
190 | /// The protocol ID |
191 | pub id: u32, |
192 | /// The interface |
193 | pub interface: &'static Interface, |
194 | /// The version |
195 | pub version: u32, |
196 | } |
197 | |
198 | /// A protocol error |
199 | /// |
200 | /// This kind of error is generated by the server if your client didn't respect |
201 | /// the protocol, after which the server will kill your connection. |
202 | #[derive (Clone, Debug)] |
203 | pub struct ProtocolError { |
204 | /// The error code associated with the error |
205 | /// |
206 | /// It should be interpreted as an instance of the `Error` enum of the |
207 | /// associated interface. |
208 | pub code: u32, |
209 | /// The id of the object that caused the error |
210 | pub object_id: u32, |
211 | /// The interface of the object that caused the error |
212 | pub object_interface: String, |
213 | /// The message sent by the server describing the error |
214 | pub message: String, |
215 | } |
216 | |
217 | /// Number of arguments that are stocked inline in a `Message` before allocating |
218 | /// |
219 | /// This is a ad-hoc number trying to reach a good balance between avoiding too many allocations |
220 | /// and keeping the stack size of `Message` small. |
221 | // Note: Keep in sync with `wayland_scanner::common::gen_write_body`. |
222 | pub const INLINE_ARGS: usize = 4; |
223 | |
224 | /// Represents a message that has been sent from some object. |
225 | #[derive (Clone, Debug)] |
226 | pub struct Message<Id, Fd> { |
227 | /// The id of the object that sent the message. |
228 | pub sender_id: Id, |
229 | /// The opcode of the message. |
230 | pub opcode: u16, |
231 | /// The arguments of the message. |
232 | pub args: smallvec::SmallVec<[Argument<Id, Fd>; INLINE_ARGS]>, |
233 | } |
234 | |
235 | impl<Id, Fd> Message<Id, Fd> { |
236 | /// Map some closure on all Fd contained in this message, to change the Fd generic parameter. |
237 | pub fn map_fd<T>(self, mut f: impl FnMut(Fd) -> T) -> Message<Id, T> { |
238 | Message { |
239 | sender_id: self.sender_id, |
240 | opcode: self.opcode, |
241 | args: self.args.into_iter().map(move |arg: Argument| arg.map_fd(&mut f)).collect(), |
242 | } |
243 | } |
244 | } |
245 | |
246 | impl<Id: PartialEq, Fd: AsRawFd> PartialEq for Message<Id, Fd> { |
247 | fn eq(&self, other: &Self) -> bool { |
248 | self.sender_id == other.sender_id && self.opcode == other.opcode && self.args == other.args |
249 | } |
250 | } |
251 | |
252 | impl<Id: Eq, Fd: AsRawFd> Eq for Message<Id, Fd> {} |
253 | |
254 | impl std::error::Error for ProtocolError {} |
255 | |
256 | impl std::fmt::Display for ProtocolError { |
257 | #[cfg_attr (coverage, coverage(off))] |
258 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { |
259 | write!( |
260 | f, |
261 | "Protocol error {} on object {}@ {}: {}" , |
262 | self.code, self.object_interface, self.object_id, self.message |
263 | ) |
264 | } |
265 | } |
266 | |
267 | /// Returns true if the two interfaces are the same. |
268 | #[inline ] |
269 | pub fn same_interface(a: &'static Interface, b: &'static Interface) -> bool { |
270 | std::ptr::eq(a, b) || a.name == b.name |
271 | } |
272 | |
273 | pub(crate) fn check_for_signature<Id, Fd>( |
274 | signature: &[ArgumentType], |
275 | args: &[Argument<Id, Fd>], |
276 | ) -> bool { |
277 | if signature.len() != args.len() { |
278 | return false; |
279 | } |
280 | for (typ: ArgumentType, arg: &Argument) in signature.iter().copied().zip(args.iter()) { |
281 | if !arg.get_type().same_type(typ) { |
282 | return false; |
283 | } |
284 | } |
285 | true |
286 | } |
287 | |
288 | #[inline ] |
289 | #[allow (dead_code)] |
290 | pub(crate) fn same_interface_or_anonymous(a: &'static Interface, b: &'static Interface) -> bool { |
291 | same_interface(a, b) || same_interface(a, &ANONYMOUS_INTERFACE) |
292 | } |
293 | |
294 | /// An enum value in the protocol. |
295 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
296 | pub enum WEnum<T> { |
297 | /// The interpreted value |
298 | Value(T), |
299 | /// The stored value does not match one defined by the protocol file |
300 | Unknown(u32), |
301 | } |
302 | |
303 | /// Error representing an unknown numeric variant for a [`WEnum`] |
304 | #[derive (Debug, Copy, Clone)] |
305 | pub struct WEnumError { |
306 | typ: &'static str, |
307 | value: u32, |
308 | } |
309 | |
310 | impl std::error::Error for WEnumError {} |
311 | |
312 | impl std::fmt::Display for WEnumError { |
313 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
314 | write!(f, "Unknown numeric value {} for enum {}" , self.value, self.typ) |
315 | } |
316 | } |
317 | |
318 | impl<T> WEnum<T> { |
319 | /// Convert this [`WEnum`] into a result |
320 | /// |
321 | /// This can be used to take advantage of the numerous helper methods on [`Result`] if you |
322 | /// don't plan to handle the unknown case of this enum. |
323 | /// |
324 | /// You can also use the [`From`] and [`Into`] traits to perform the same conversion. |
325 | #[inline ] |
326 | pub fn into_result(self) -> Result<T, WEnumError> { |
327 | match self { |
328 | Self::Value(v: T) => Ok(v), |
329 | Self::Unknown(value: u32) => Err(WEnumError { typ: std::any::type_name::<T>(), value }), |
330 | } |
331 | } |
332 | } |
333 | |
334 | impl<T> From<WEnum<T>> for Result<T, WEnumError> { |
335 | fn from(me: WEnum<T>) -> Self { |
336 | me.into_result() |
337 | } |
338 | } |
339 | |
340 | impl<T: TryFrom<u32>> From<u32> for WEnum<T> { |
341 | /// Constructs an enum from the integer format used by the wayland protocol. |
342 | fn from(v: u32) -> Self { |
343 | match T::try_from(v) { |
344 | Ok(t: T) => Self::Value(t), |
345 | Err(_) => Self::Unknown(v), |
346 | } |
347 | } |
348 | } |
349 | |
350 | impl<T: Into<u32>> From<WEnum<T>> for u32 { |
351 | /// Converts an enum into a numerical form used by the wayland protocol. |
352 | fn from(enu: WEnum<T>) -> u32 { |
353 | match enu { |
354 | WEnum::Unknown(u: u32) => u, |
355 | WEnum::Value(t: T) => t.into(), |
356 | } |
357 | } |
358 | } |
359 | |