| 1 | //! Types and utilities for manipulating the Wayland protocol |
| 2 | |
| 3 | use std::{ffi::CString, os::unix::io::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, " {:.4}" , *value as f64 / 256.0), |
| 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 | |