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