Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
71
third-party/vendor/wayland-commons/src/debug.rs
vendored
Normal file
71
third-party/vendor/wayland-commons/src/debug.rs
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
//! Debugging helpers to handle `WAYLAND_DEBUG` env variable.
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::wire::Argument;
|
||||
|
||||
/// Print the dispatched message to stderr in a following format:
|
||||
///
|
||||
/// [timestamp] <- interface@id.msg_name(args)
|
||||
pub fn print_dispatched_message(interface: &str, id: u32, msg_name: &str, args: &[Argument]) {
|
||||
// Add timestamp to output.
|
||||
print_timestamp();
|
||||
|
||||
eprint!(" <- {}@{}.{}", interface, id, msg_name);
|
||||
|
||||
print_args(args);
|
||||
|
||||
// Add a new line.
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
/// Print the send message to stderr in a following format:
|
||||
///
|
||||
/// [timestamp] -> interface@id.msg_name(args)
|
||||
///
|
||||
/// If `is_alive` is `false` the `[ZOMBIE]` is added after `id`.
|
||||
pub fn print_send_message(
|
||||
interface: &str,
|
||||
id: u32,
|
||||
is_alive: bool,
|
||||
msg_name: &str,
|
||||
args: &[Argument],
|
||||
) {
|
||||
// Add timestamp to output.
|
||||
print_timestamp();
|
||||
|
||||
eprint!(" -> {}@{}{}.{}", interface, id, if is_alive { "" } else { "[ZOMBIE]" }, msg_name);
|
||||
|
||||
print_args(args);
|
||||
|
||||
// Add a new line.
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
/// Print arguments with opening/closing bracket.
|
||||
fn print_args(args: &[Argument]) {
|
||||
let num_args = args.len();
|
||||
|
||||
eprint!("(");
|
||||
|
||||
if num_args > 0 {
|
||||
// Explicitly handle first argument to handle one arg functions nicely.
|
||||
eprint!("{}", args[0]);
|
||||
|
||||
// Handle the rest.
|
||||
for arg in args.iter().take(num_args).skip(1) {
|
||||
eprint!(", {}", arg);
|
||||
}
|
||||
}
|
||||
|
||||
eprint!(")")
|
||||
}
|
||||
|
||||
/// Print timestamp in seconds.microseconds format.
|
||||
fn print_timestamp() {
|
||||
if let Ok(timestamp) = SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
let sc = timestamp.as_secs();
|
||||
let ms = timestamp.subsec_micros();
|
||||
eprint!("[{}.{:06}]", sc, ms);
|
||||
}
|
||||
}
|
||||
109
third-party/vendor/wayland-commons/src/filter.rs
vendored
Normal file
109
third-party/vendor/wayland-commons/src/filter.rs
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
//! Filter
|
||||
|
||||
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
|
||||
|
||||
/// Holder of global dispatch-related data
|
||||
///
|
||||
/// This struct serves as a dynamic container for the dispatch-time
|
||||
/// global data that you gave to the dispatch method, and is given as
|
||||
/// input to all your callbacks. It allows you to share global state
|
||||
/// between your filters.
|
||||
///
|
||||
/// The main method of interest is the `get` method, which allows you to
|
||||
/// access a `&mut _` reference to the global data itself. The other methods
|
||||
/// are mostly used internally by the crate.
|
||||
pub struct DispatchData<'a> {
|
||||
data: &'a mut dyn std::any::Any,
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for DispatchData<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("DispatchData { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DispatchData<'a> {
|
||||
/// Access the dispatch data knowing its type
|
||||
///
|
||||
/// Will return `None` if the provided type is not the correct
|
||||
/// inner type.
|
||||
pub fn get<T: std::any::Any>(&mut self) -> Option<&mut T> {
|
||||
self.data.downcast_mut()
|
||||
}
|
||||
|
||||
/// Wrap a mutable reference
|
||||
///
|
||||
/// This creates a new `DispatchData` from a mutable reference
|
||||
pub fn wrap<T: std::any::Any>(data: &'a mut T) -> DispatchData<'a> {
|
||||
DispatchData { data }
|
||||
}
|
||||
|
||||
/// Reborrows this `DispatchData` to create a new one with the same content
|
||||
///
|
||||
/// This is a quick and cheap way to propagate the `DispatchData` down a
|
||||
/// callback stack by value. It is basically a noop only there to ease
|
||||
/// work with the borrow checker.
|
||||
pub fn reborrow(&mut self) -> DispatchData {
|
||||
DispatchData { data: &mut *self.data }
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner<E, F: ?Sized> {
|
||||
pending: RefCell<VecDeque<E>>,
|
||||
cb: RefCell<F>,
|
||||
}
|
||||
|
||||
type DynInner<E> = Inner<E, dyn FnMut(E, &Filter<E>, DispatchData<'_>)>;
|
||||
|
||||
/// An event filter
|
||||
///
|
||||
/// Can be used in wayland-client and wayland-server to aggregate
|
||||
/// messages from different objects into the same closure.
|
||||
///
|
||||
/// You need to provide it a closure of type `FnMut(E, &Filter<E>)`,
|
||||
/// which will be called any time a message is sent to the filter
|
||||
/// via the `send(..)` method. Your closure also receives a handle
|
||||
/// to the filter as argument, so that you can use it from within
|
||||
/// the callback (to assign new wayland objects to this filter for
|
||||
/// example).
|
||||
///
|
||||
/// The `Filter` can be cloned, and all clones send messages to the
|
||||
/// same closure. However it is not threadsafe.
|
||||
pub struct Filter<E> {
|
||||
inner: Rc<DynInner<E>>,
|
||||
}
|
||||
|
||||
impl<E: std::fmt::Debug> std::fmt::Debug for Filter<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Filter").field("pending", &self.inner.pending).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Clone for Filter<E> {
|
||||
fn clone(&self) -> Filter<E> {
|
||||
Filter { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Filter<E> {
|
||||
/// Create a new filter from given closure
|
||||
pub fn new<F: FnMut(E, &Filter<E>, DispatchData<'_>) + 'static>(f: F) -> Filter<E> {
|
||||
Filter {
|
||||
inner: Rc::new(Inner { pending: RefCell::new(VecDeque::new()), cb: RefCell::new(f) }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to this filter
|
||||
pub fn send(&self, evt: E, mut data: DispatchData) {
|
||||
// gracefully handle reentrancy
|
||||
if let Ok(mut guard) = self.inner.cb.try_borrow_mut() {
|
||||
(&mut *guard)(evt, self, data.reborrow());
|
||||
// process all events that might have been enqueued by the cb
|
||||
while let Some(evt) = self.inner.pending.borrow_mut().pop_front() {
|
||||
(&mut *guard)(evt, self, data.reborrow());
|
||||
}
|
||||
} else {
|
||||
self.inner.pending.borrow_mut().push_back(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
228
third-party/vendor/wayland-commons/src/lib.rs
vendored
Normal file
228
third-party/vendor/wayland-commons/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
//! Common definitions for wayland
|
||||
//!
|
||||
//! This crate hosts common type and traits used to represent wayland messages
|
||||
//! and routines in the `wayland-client` and `wayland-server` crates.
|
||||
//!
|
||||
//! This notably includes the `Interface` trait, which can exhaustively describe
|
||||
//! any wayland interface. Its implementations are intended to be generated by the
|
||||
//! `wayland-scanner` crate.
|
||||
//!
|
||||
//! The principal user-facing definition provided by this crate is the `Implementation`
|
||||
//! trait, which as a user of `wayland-client` or `wayland-server` you will be using
|
||||
//! to define objects able to handle the messages your program receives. Note that
|
||||
//! this trait is auto-implemented for closures with appropriate signature, for
|
||||
//! convenience.
|
||||
|
||||
#![warn(missing_docs, missing_debug_implementations)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nix;
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use wayland_sys::common as syscom;
|
||||
|
||||
pub mod debug;
|
||||
pub mod filter;
|
||||
pub mod map;
|
||||
pub mod socket;
|
||||
pub mod user_data;
|
||||
pub mod wire;
|
||||
|
||||
pub use smallvec::smallvec;
|
||||
|
||||
/// A group of messages
|
||||
///
|
||||
/// This represents a group of message that can be serialized on the protocol wire.
|
||||
/// Typically the set of events or requests of a single interface.
|
||||
///
|
||||
/// Implementations of this trait are supposed to be
|
||||
/// generated using the `wayland-scanner` crate.
|
||||
pub trait MessageGroup: Sized {
|
||||
/// Wire representation of this MessageGroup
|
||||
const MESSAGES: &'static [wire::MessageDesc];
|
||||
/// The wrapper type for ObjectMap allowing the mapping of Object and
|
||||
/// NewId arguments to the object map during parsing.
|
||||
type Map;
|
||||
/// The opcode of this message
|
||||
fn opcode(&self) -> u16;
|
||||
/// Whether this message is a destructor
|
||||
///
|
||||
/// If it is, once send or receive the associated object cannot be used any more.
|
||||
fn is_destructor(&self) -> bool;
|
||||
/// The minimal object version for which this message exists
|
||||
fn since(&self) -> u32;
|
||||
/// Retrieve the child `Object` associated with this message if any
|
||||
fn child<Meta: self::map::ObjectMetadata>(
|
||||
opcode: u16,
|
||||
version: u32,
|
||||
meta: &Meta,
|
||||
) -> Option<crate::map::Object<Meta>>;
|
||||
/// Construct a message from its raw representation
|
||||
// -- The lint is allowed because fixing it would be a breaking change --
|
||||
#[allow(clippy::result_unit_err)]
|
||||
fn from_raw(msg: wire::Message, map: &mut Self::Map) -> Result<Self, ()>;
|
||||
/// Turn this message into its raw representation
|
||||
fn into_raw(self, send_id: u32) -> wire::Message;
|
||||
/// Construct a message of this group from its C representation
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The pointers provided to this function must all be valid pointers from
|
||||
/// `libwayland-client`
|
||||
// -- The lint is allowed because fixing it would be a breaking change --
|
||||
#[allow(clippy::result_unit_err)]
|
||||
unsafe fn from_raw_c(
|
||||
obj: *mut c_void,
|
||||
opcode: u32,
|
||||
args: *const syscom::wl_argument,
|
||||
) -> Result<Self, ()>;
|
||||
/// Build a C representation of this message
|
||||
///
|
||||
/// It can only be accessed from the provided closure, and this consumes
|
||||
/// the message.
|
||||
// -- The lint is allowed because fixing it would be a breaking change --
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn as_raw_c_in<F, T>(self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(u32, &mut [syscom::wl_argument]) -> T;
|
||||
}
|
||||
|
||||
/// The description of a wayland interface
|
||||
///
|
||||
/// Implementations of this trait are supposed to be
|
||||
/// generated using the `wayland-scanner` crate.
|
||||
pub trait Interface: 'static {
|
||||
/// Set of requests associated to this interface
|
||||
///
|
||||
/// Requests are messages from the client to the server
|
||||
type Request: MessageGroup + 'static;
|
||||
/// Set of events associated to this interface
|
||||
///
|
||||
/// Events are messages from the server to the client
|
||||
type Event: MessageGroup + 'static;
|
||||
/// Name of this interface
|
||||
const NAME: &'static str;
|
||||
/// Maximum supported version of this interface
|
||||
///
|
||||
/// This is the maximum version supported by the protocol specification currently
|
||||
/// used by this library, and should not be used as-is in your code, as a version
|
||||
/// change can subtly change the behavior of some objects.
|
||||
///
|
||||
/// Server are supposed to be able to handle all versions from 1 to the one they
|
||||
/// advertise through the registry, and clients can choose any version among the
|
||||
/// ones the server supports.
|
||||
const VERSION: u32;
|
||||
/// Pointer to the C representation of this interface
|
||||
fn c_interface() -> *const syscom::wl_interface;
|
||||
}
|
||||
|
||||
/// An empty enum representing a MessageGroup with no messages
|
||||
#[derive(Debug)]
|
||||
pub enum NoMessage {}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl MessageGroup for NoMessage {
|
||||
const MESSAGES: &'static [wire::MessageDesc] = &[];
|
||||
type Map = ();
|
||||
fn is_destructor(&self) -> bool {
|
||||
match *self {}
|
||||
}
|
||||
fn opcode(&self) -> u16 {
|
||||
match *self {}
|
||||
}
|
||||
fn since(&self) -> u32 {
|
||||
match *self {}
|
||||
}
|
||||
fn child<M: self::map::ObjectMetadata>(_: u16, _: u32, _: &M) -> Option<crate::map::Object<M>> {
|
||||
None
|
||||
}
|
||||
fn from_raw(_: wire::Message, _: &mut ()) -> Result<Self, ()> {
|
||||
Err(())
|
||||
}
|
||||
fn into_raw(self, _: u32) -> wire::Message {
|
||||
match self {}
|
||||
}
|
||||
unsafe fn from_raw_c(
|
||||
_obj: *mut c_void,
|
||||
_opcode: u32,
|
||||
_args: *const syscom::wl_argument,
|
||||
) -> Result<Self, ()> {
|
||||
Err(())
|
||||
}
|
||||
fn as_raw_c_in<F, T>(self, _f: F) -> T
|
||||
where
|
||||
F: FnOnce(u32, &mut [syscom::wl_argument]) -> T,
|
||||
{
|
||||
match self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a value in a threadafe container that
|
||||
/// only lets you access it from its owning thread
|
||||
///
|
||||
/// If the ThreadGuard is dropped from the wrong thread,
|
||||
/// the underlying value will be leaked.
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadGuard<T: ?Sized> {
|
||||
thread: std::thread::ThreadId,
|
||||
val: std::mem::ManuallyDrop<T>,
|
||||
}
|
||||
|
||||
impl<T> ThreadGuard<T> {
|
||||
/// Create a new ThreadGuard wrapper
|
||||
pub fn new(val: T) -> ThreadGuard<T> {
|
||||
ThreadGuard { val: std::mem::ManuallyDrop::new(val), thread: std::thread::current().id() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> ThreadGuard<T> {
|
||||
/// Access the underlying value
|
||||
///
|
||||
/// Panics if done on the wrong thread
|
||||
pub fn get(&self) -> &T {
|
||||
self.try_get().expect("Attempted to access a ThreadGuard contents from the wrong thread.")
|
||||
}
|
||||
|
||||
/// Mutably access the underlying value
|
||||
///
|
||||
/// Panics if done on the wrong thread
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.try_get_mut()
|
||||
.expect("Attempted to access a ThreadGuard contents from the wrong thread.")
|
||||
}
|
||||
|
||||
/// Try to access the underlying value
|
||||
///
|
||||
/// Returns `None` if done on the wrong thread
|
||||
pub fn try_get(&self) -> Option<&T> {
|
||||
if self.thread == ::std::thread::current().id() {
|
||||
Some(&self.val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to mutably access the underlying value
|
||||
///
|
||||
/// Returns `None` if done on the wrong thread
|
||||
pub fn try_get_mut(&mut self) -> Option<&mut T> {
|
||||
if self.thread == ::std::thread::current().id() {
|
||||
Some(&mut self.val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for ThreadGuard<T> {
|
||||
fn drop(&mut self) {
|
||||
// We can only actually perform the drop if we are on the right thread
|
||||
// otherwise it may be racy, so we just leak the value
|
||||
if self.thread == ::std::thread::current().id() {
|
||||
unsafe { std::mem::ManuallyDrop::drop(&mut self.val) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized> Send for ThreadGuard<T> {}
|
||||
unsafe impl<T: ?Sized> Sync for ThreadGuard<T> {}
|
||||
252
third-party/vendor/wayland-commons/src/map.rs
vendored
Normal file
252
third-party/vendor/wayland-commons/src/map.rs
vendored
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
//! Wayland objects map
|
||||
use crate::{Interface, MessageGroup, NoMessage};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Limit separating server-created from client-created objects IDs in the namespace
|
||||
pub const SERVER_ID_LIMIT: u32 = 0xFF00_0000;
|
||||
|
||||
/// A trait representing the metadata a wayland implementation
|
||||
/// may attach to an object.
|
||||
pub trait ObjectMetadata: Clone {
|
||||
/// Create the metadata for a child object
|
||||
///
|
||||
/// Mostly needed for client side, to propagate the event queues
|
||||
fn child(&self) -> Self;
|
||||
}
|
||||
|
||||
impl ObjectMetadata for () {
|
||||
fn child(&self) {}
|
||||
}
|
||||
|
||||
/// The representation of a protocol object
|
||||
#[derive(Clone)]
|
||||
pub struct Object<Meta: ObjectMetadata> {
|
||||
/// Interface name of this object
|
||||
pub interface: &'static str,
|
||||
/// Version of this object
|
||||
pub version: u32,
|
||||
/// Description of the requests of this object
|
||||
pub requests: &'static [crate::wire::MessageDesc],
|
||||
/// Description of the events of this object
|
||||
pub events: &'static [crate::wire::MessageDesc],
|
||||
/// Metadata associated to this object (ex: its event queue client side)
|
||||
pub meta: Meta,
|
||||
/// A function which, from an opcode, a version, and the Meta, creates a child
|
||||
/// object associated with this event if any
|
||||
pub childs_from_events: fn(u16, u32, &Meta) -> Option<Object<Meta>>,
|
||||
/// A function which, from an opcode, a version, and the Meta, creates a child
|
||||
/// object associated with this request if any
|
||||
pub childs_from_requests: fn(u16, u32, &Meta) -> Option<Object<Meta>>,
|
||||
}
|
||||
|
||||
impl<Meta: ObjectMetadata + std::fmt::Debug> std::fmt::Debug for Object<Meta> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Object")
|
||||
.field("interface", &self.interface)
|
||||
.field("version", &self.version)
|
||||
.field("requests", &self.requests)
|
||||
.field("events", &self.events)
|
||||
.field("meta", &self.meta)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Meta: ObjectMetadata> Object<Meta> {
|
||||
/// Create an Object corresponding to given interface and version
|
||||
pub fn from_interface<I: Interface>(version: u32, meta: Meta) -> Object<Meta> {
|
||||
Object {
|
||||
interface: I::NAME,
|
||||
version,
|
||||
requests: I::Request::MESSAGES,
|
||||
events: I::Event::MESSAGES,
|
||||
meta,
|
||||
childs_from_events: childs_from::<I::Event, Meta>,
|
||||
childs_from_requests: childs_from::<I::Request, Meta>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an optional `Object` corresponding to the possible `new_id` associated
|
||||
/// with given event opcode
|
||||
pub fn event_child(&self, opcode: u16) -> Option<Object<Meta>> {
|
||||
(self.childs_from_events)(opcode, self.version, &self.meta)
|
||||
}
|
||||
|
||||
/// Create an optional `Object` corresponding to the possible `new_id` associated
|
||||
/// with given request opcode
|
||||
pub fn request_child(&self, opcode: u16) -> Option<Object<Meta>> {
|
||||
(self.childs_from_requests)(opcode, self.version, &self.meta)
|
||||
}
|
||||
|
||||
/// Check whether this object is of given interface
|
||||
pub fn is_interface<I: Interface>(&self) -> bool {
|
||||
// TODO: we might want to be more robust than that
|
||||
self.interface == I::NAME
|
||||
}
|
||||
|
||||
/// Create a placeholder object that will be filled-in by the message logic
|
||||
pub fn placeholder(meta: Meta) -> Object<Meta> {
|
||||
Object {
|
||||
interface: "",
|
||||
version: 0,
|
||||
requests: &[],
|
||||
events: &[],
|
||||
meta,
|
||||
childs_from_events: childs_from::<NoMessage, Meta>,
|
||||
childs_from_requests: childs_from::<NoMessage, Meta>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn childs_from<M: MessageGroup, Meta: ObjectMetadata>(
|
||||
opcode: u16,
|
||||
version: u32,
|
||||
meta: &Meta,
|
||||
) -> Option<Object<Meta>> {
|
||||
M::child(opcode, version, meta)
|
||||
}
|
||||
|
||||
/// A holder for the object store of a connection
|
||||
///
|
||||
/// Keeps track of which object id is associated to which
|
||||
/// interface object, and which is currently unused.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ObjectMap<Meta: ObjectMetadata> {
|
||||
client_objects: Vec<Option<Object<Meta>>>,
|
||||
server_objects: Vec<Option<Object<Meta>>>,
|
||||
}
|
||||
|
||||
impl<Meta: ObjectMetadata> ObjectMap<Meta> {
|
||||
/// Create a new empty object map
|
||||
pub fn new() -> ObjectMap<Meta> {
|
||||
ObjectMap { client_objects: Vec::new(), server_objects: Vec::new() }
|
||||
}
|
||||
|
||||
/// Find an object in the store
|
||||
pub fn find(&self, id: u32) -> Option<Object<Meta>> {
|
||||
if id == 0 {
|
||||
None
|
||||
} else if id >= SERVER_ID_LIMIT {
|
||||
self.server_objects.get((id - SERVER_ID_LIMIT) as usize).and_then(Clone::clone)
|
||||
} else {
|
||||
self.client_objects.get((id - 1) as usize).and_then(Clone::clone)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an object from the store
|
||||
///
|
||||
/// Does nothing if the object didn't previously exists
|
||||
pub fn remove(&mut self, id: u32) {
|
||||
if id == 0 {
|
||||
// nothing
|
||||
} else if id >= SERVER_ID_LIMIT {
|
||||
if let Some(place) = self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize) {
|
||||
*place = None;
|
||||
}
|
||||
} else if let Some(place) = self.client_objects.get_mut((id - 1) as usize) {
|
||||
*place = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert given object for given id
|
||||
///
|
||||
/// Can fail if the requested id is not the next free id of this store.
|
||||
/// (In which case this is a protocol error)
|
||||
// -- The lint is allowed because fixing it would be a breaking change --
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn insert_at(&mut self, id: u32, object: Object<Meta>) -> Result<(), ()> {
|
||||
if id == 0 {
|
||||
Err(())
|
||||
} else if id >= SERVER_ID_LIMIT {
|
||||
insert_in_at(&mut self.server_objects, (id - SERVER_ID_LIMIT) as usize, object)
|
||||
} else {
|
||||
insert_in_at(&mut self.client_objects, (id - 1) as usize, object)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a new id for an object in the client namespace
|
||||
pub fn client_insert_new(&mut self, object: Object<Meta>) -> u32 {
|
||||
insert_in(&mut self.client_objects, object) + 1
|
||||
}
|
||||
|
||||
/// Allocate a new id for an object in the server namespace
|
||||
pub fn server_insert_new(&mut self, object: Object<Meta>) -> u32 {
|
||||
insert_in(&mut self.server_objects, object) + SERVER_ID_LIMIT
|
||||
}
|
||||
|
||||
/// Mutably access an object of the map
|
||||
// -- The lint is allowed because fixing it would be a breaking change --
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn with<T, F: FnOnce(&mut Object<Meta>) -> T>(&mut self, id: u32, f: F) -> Result<T, ()> {
|
||||
if id == 0 {
|
||||
Err(())
|
||||
} else if id >= SERVER_ID_LIMIT {
|
||||
if let Some(&mut Some(ref mut obj)) =
|
||||
self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize)
|
||||
{
|
||||
Ok(f(obj))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else if let Some(&mut Some(ref mut obj)) = self.client_objects.get_mut((id - 1) as usize)
|
||||
{
|
||||
Ok(f(obj))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutably access all objects of the map in sequence
|
||||
pub fn with_all<F: FnMut(u32, &mut Object<Meta>)>(&mut self, mut f: F) {
|
||||
for (id, place) in self.client_objects.iter_mut().enumerate() {
|
||||
if let Some(ref mut obj) = *place {
|
||||
f(id as u32 + 1, obj);
|
||||
}
|
||||
}
|
||||
for (id, place) in self.server_objects.iter_mut().enumerate() {
|
||||
if let Some(ref mut obj) = *place {
|
||||
f(id as u32 + SERVER_ID_LIMIT, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert a new object in a store at the first free place
|
||||
fn insert_in<Meta: ObjectMetadata>(
|
||||
store: &mut Vec<Option<Object<Meta>>>,
|
||||
object: Object<Meta>,
|
||||
) -> u32 {
|
||||
match store.iter().position(Option::is_none) {
|
||||
Some(id) => {
|
||||
store[id] = Some(object);
|
||||
id as u32
|
||||
}
|
||||
None => {
|
||||
store.push(Some(object));
|
||||
(store.len() - 1) as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert an object at a given place in a store
|
||||
fn insert_in_at<Meta: ObjectMetadata>(
|
||||
store: &mut Vec<Option<Object<Meta>>>,
|
||||
id: usize,
|
||||
object: Object<Meta>,
|
||||
) -> Result<(), ()> {
|
||||
match id.cmp(&store.len()) {
|
||||
Ordering::Greater => Err(()),
|
||||
Ordering::Equal => {
|
||||
store.push(Some(object));
|
||||
Ok(())
|
||||
}
|
||||
Ordering::Less => {
|
||||
let previous = &mut store[id];
|
||||
if !previous.is_none() {
|
||||
return Err(());
|
||||
}
|
||||
*previous = Some(object);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
681
third-party/vendor/wayland-commons/src/socket.rs
vendored
Normal file
681
third-party/vendor/wayland-commons/src/socket.rs
vendored
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
//! Wayland socket manipulation
|
||||
|
||||
use std::io::{IoSlice, IoSliceMut};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
|
||||
use nix::{sys::socket, Result as NixResult};
|
||||
|
||||
use crate::wire::{ArgumentType, Message, MessageParseError, MessageWriteError};
|
||||
|
||||
/// Maximum number of FD that can be sent in a single socket message
|
||||
pub const MAX_FDS_OUT: usize = 28;
|
||||
/// Maximum number of bytes that can be sent in a single socket message
|
||||
pub const MAX_BYTES_OUT: usize = 4096;
|
||||
|
||||
/*
|
||||
* Socket
|
||||
*/
|
||||
|
||||
/// A wayland socket
|
||||
#[derive(Debug)]
|
||||
pub struct Socket {
|
||||
fd: RawFd,
|
||||
}
|
||||
|
||||
impl Socket {
|
||||
/// Send a single message to the socket
|
||||
///
|
||||
/// A single socket message can contain several wayland messages
|
||||
///
|
||||
/// The `fds` slice should not be longer than `MAX_FDS_OUT`, and the `bytes`
|
||||
/// slice should not be longer than `MAX_BYTES_OUT` otherwise the receiving
|
||||
/// end may lose some data.
|
||||
pub fn send_msg(&self, bytes: &[u8], fds: &[RawFd]) -> NixResult<()> {
|
||||
let flags = socket::MsgFlags::MSG_DONTWAIT | socket::MsgFlags::MSG_NOSIGNAL;
|
||||
let iov = [IoSlice::new(bytes)];
|
||||
|
||||
if !fds.is_empty() {
|
||||
let cmsgs = [socket::ControlMessage::ScmRights(fds)];
|
||||
socket::sendmsg::<()>(self.fd, &iov, &cmsgs, flags, None)?;
|
||||
} else {
|
||||
socket::sendmsg::<()>(self.fd, &iov, &[], flags, None)?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive a single message from the socket
|
||||
///
|
||||
/// Return the number of bytes received and the number of Fds received.
|
||||
///
|
||||
/// Errors with `WouldBlock` is no message is available.
|
||||
///
|
||||
/// A single socket message can contain several wayland messages.
|
||||
///
|
||||
/// The `buffer` slice should be at least `MAX_BYTES_OUT` long and the `fds`
|
||||
/// slice `MAX_FDS_OUT` long, otherwise some data of the received message may
|
||||
/// be lost.
|
||||
pub fn rcv_msg(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> NixResult<(usize, usize)> {
|
||||
let mut cmsg = cmsg_space!([RawFd; MAX_FDS_OUT]);
|
||||
let mut iov = [IoSliceMut::new(buffer)];
|
||||
|
||||
let msg = socket::recvmsg::<()>(
|
||||
self.fd,
|
||||
&mut iov[..],
|
||||
Some(&mut cmsg),
|
||||
socket::MsgFlags::MSG_DONTWAIT
|
||||
| socket::MsgFlags::MSG_CMSG_CLOEXEC
|
||||
| socket::MsgFlags::MSG_NOSIGNAL,
|
||||
)?;
|
||||
|
||||
let mut fd_count = 0;
|
||||
let received_fds = msg.cmsgs().flat_map(|cmsg| match cmsg {
|
||||
socket::ControlMessageOwned::ScmRights(s) => s,
|
||||
_ => Vec::new(),
|
||||
});
|
||||
for (fd, place) in received_fds.zip(fds.iter_mut()) {
|
||||
fd_count += 1;
|
||||
*place = fd;
|
||||
}
|
||||
Ok((msg.bytes, fd_count))
|
||||
}
|
||||
|
||||
/// Retrieve the current value of the requested [socket::GetSockOpt]
|
||||
pub fn opt<O: socket::GetSockOpt>(&self, opt: O) -> NixResult<O::Val> {
|
||||
socket::getsockopt(self.fd, opt)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for Socket {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Socket {
|
||||
Socket { fd }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Socket {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Socket {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Socket {
|
||||
fn drop(&mut self) {
|
||||
let _ = ::nix::unistd::close(self.fd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BufferedSocket
|
||||
*/
|
||||
|
||||
/// An adapter around a raw Socket that directly handles buffering and
|
||||
/// conversion from/to wayland messages
|
||||
#[derive(Debug)]
|
||||
pub struct BufferedSocket {
|
||||
socket: Socket,
|
||||
in_data: Buffer<u32>,
|
||||
in_fds: Buffer<RawFd>,
|
||||
out_data: Buffer<u32>,
|
||||
out_fds: Buffer<RawFd>,
|
||||
}
|
||||
|
||||
impl BufferedSocket {
|
||||
/// Wrap a Socket into a Buffered Socket
|
||||
pub fn new(socket: Socket) -> BufferedSocket {
|
||||
BufferedSocket {
|
||||
socket,
|
||||
in_data: Buffer::new(2 * MAX_BYTES_OUT / 4), // Incoming buffers are twice as big in order to be
|
||||
in_fds: Buffer::new(2 * MAX_FDS_OUT), // able to store leftover data if needed
|
||||
out_data: Buffer::new(MAX_BYTES_OUT / 4),
|
||||
out_fds: Buffer::new(MAX_FDS_OUT),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get direct access to the underlying socket
|
||||
pub fn get_socket(&mut self) -> &mut Socket {
|
||||
&mut self.socket
|
||||
}
|
||||
|
||||
/// Retrieve ownership of the underlying Socket
|
||||
///
|
||||
/// Any leftover content in the internal buffers will be lost
|
||||
pub fn into_socket(self) -> Socket {
|
||||
self.socket
|
||||
}
|
||||
|
||||
/// Flush the contents of the outgoing buffer into the socket
|
||||
pub fn flush(&mut self) -> NixResult<()> {
|
||||
{
|
||||
let words = self.out_data.get_contents();
|
||||
if words.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let bytes = unsafe {
|
||||
::std::slice::from_raw_parts(words.as_ptr() as *const u8, words.len() * 4)
|
||||
};
|
||||
let fds = self.out_fds.get_contents();
|
||||
self.socket.send_msg(bytes, fds)?;
|
||||
for &fd in fds {
|
||||
// once the fds are sent, we can close them
|
||||
let _ = ::nix::unistd::close(fd);
|
||||
}
|
||||
}
|
||||
self.out_data.clear();
|
||||
self.out_fds.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// internal method
|
||||
//
|
||||
// attempts to write a message in the internal out buffers,
|
||||
// returns true if successful
|
||||
//
|
||||
// if false is returned, it means there is not enough space
|
||||
// in the buffer
|
||||
fn attempt_write_message(&mut self, msg: &Message) -> NixResult<bool> {
|
||||
match msg.write_to_buffers(
|
||||
self.out_data.get_writable_storage(),
|
||||
self.out_fds.get_writable_storage(),
|
||||
) {
|
||||
Ok((bytes_out, fds_out)) => {
|
||||
self.out_data.advance(bytes_out);
|
||||
self.out_fds.advance(fds_out);
|
||||
Ok(true)
|
||||
}
|
||||
Err(MessageWriteError::BufferTooSmall) => Ok(false),
|
||||
Err(MessageWriteError::DupFdFailed(e)) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a message to the outgoing buffer
|
||||
///
|
||||
/// This method may flush the internal buffer if necessary (if it is full).
|
||||
///
|
||||
/// If the message is too big to fit in the buffer, the error `Error::Sys(E2BIG)`
|
||||
/// will be returned.
|
||||
pub fn write_message(&mut self, msg: &Message) -> NixResult<()> {
|
||||
if !self.attempt_write_message(msg)? {
|
||||
// the attempt failed, there is not enough space in the buffer
|
||||
// we need to flush it
|
||||
self.flush()?;
|
||||
if !self.attempt_write_message(msg)? {
|
||||
// If this fails again, this means the message is too big
|
||||
// to be transmitted at all
|
||||
return Err(::nix::Error::E2BIG);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Try to fill the incoming buffers of this socket, to prepare
|
||||
/// a new round of parsing.
|
||||
pub fn fill_incoming_buffers(&mut self) -> NixResult<()> {
|
||||
// clear the buffers if they have no content
|
||||
if !self.in_data.has_content() {
|
||||
self.in_data.clear();
|
||||
}
|
||||
if !self.in_fds.has_content() {
|
||||
self.in_fds.clear();
|
||||
}
|
||||
// receive a message
|
||||
let (in_bytes, in_fds) = {
|
||||
let words = self.in_data.get_writable_storage();
|
||||
let bytes = unsafe {
|
||||
::std::slice::from_raw_parts_mut(words.as_ptr() as *mut u8, words.len() * 4)
|
||||
};
|
||||
let fds = self.in_fds.get_writable_storage();
|
||||
self.socket.rcv_msg(bytes, fds)?
|
||||
};
|
||||
if in_bytes == 0 {
|
||||
// the other end of the socket was closed
|
||||
return Err(::nix::Error::EPIPE);
|
||||
}
|
||||
// advance the storage
|
||||
self.in_data.advance(in_bytes / 4 + if in_bytes % 4 > 0 { 1 } else { 0 });
|
||||
self.in_fds.advance(in_fds);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read and deserialize a single message from the incoming buffers socket
|
||||
///
|
||||
/// This method requires one closure that given an object id and an opcode,
|
||||
/// must provide the signature of the associated request/event, in the form of
|
||||
/// a `&'static [ArgumentType]`. If it returns `None`, meaning that
|
||||
/// the couple object/opcode does not exist, an error will be returned.
|
||||
///
|
||||
/// There are 3 possibilities of return value:
|
||||
///
|
||||
/// - `Ok(Ok(msg))`: no error occurred, this is the message
|
||||
/// - `Ok(Err(e))`: either a malformed message was encountered or we need more data,
|
||||
/// in the latter case you need to try calling `fill_incoming_buffers()`.
|
||||
/// - `Err(e)`: an I/O error occurred reading from the socked, details are in `e`
|
||||
/// (this can be a "wouldblock" error, which just means that no message is available
|
||||
/// to read)
|
||||
pub fn read_one_message<F>(&mut self, mut signature: F) -> Result<Message, MessageParseError>
|
||||
where
|
||||
F: FnMut(u32, u16) -> Option<&'static [ArgumentType]>,
|
||||
{
|
||||
let (msg, read_data, read_fd) = {
|
||||
let data = self.in_data.get_contents();
|
||||
let fds = self.in_fds.get_contents();
|
||||
if data.len() < 2 {
|
||||
return Err(MessageParseError::MissingData);
|
||||
}
|
||||
let object_id = data[0];
|
||||
let opcode = (data[1] & 0x0000_FFFF) as u16;
|
||||
if let Some(sig) = signature(object_id, opcode) {
|
||||
match Message::from_raw(data, sig, fds) {
|
||||
Ok((msg, rest_data, rest_fds)) => {
|
||||
(msg, data.len() - rest_data.len(), fds.len() - rest_fds.len())
|
||||
}
|
||||
// TODO: gracefully handle wayland messages split across unix messages ?
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
// no signature found ?
|
||||
return Err(MessageParseError::Malformed);
|
||||
}
|
||||
};
|
||||
|
||||
self.in_data.offset(read_data);
|
||||
self.in_fds.offset(read_fd);
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Read and deserialize messages from the socket
|
||||
///
|
||||
/// This method requires two closures:
|
||||
///
|
||||
/// - The first one, given an object id and an opcode, must provide
|
||||
/// the signature of the associated request/event, in the form of
|
||||
/// a `&'static [ArgumentType]`. If it returns `None`, meaning that
|
||||
/// the couple object/opcode does not exist, the parsing will be
|
||||
/// prematurely interrupted and this method will return a
|
||||
/// `MessageParseError::Malformed` error.
|
||||
/// - The second closure is charged to process the parsed message. If it
|
||||
/// returns `false`, the iteration will be prematurely stopped.
|
||||
///
|
||||
/// In both cases of early stopping, the remaining unused data will be left
|
||||
/// in the buffers, and will start to be processed at the next call of this
|
||||
/// method.
|
||||
///
|
||||
/// There are 3 possibilities of return value:
|
||||
///
|
||||
/// - `Ok(Ok(n))`: no error occurred, `n` messages where processed
|
||||
/// - `Ok(Err(MessageParseError::Malformed))`: a malformed message was encountered
|
||||
/// (this is a protocol error and is supposed to be fatal to the connection).
|
||||
/// - `Err(e)`: an I/O error occurred reading from the socked, details are in `e`
|
||||
/// (this can be a "wouldblock" error, which just means that no message is available
|
||||
/// to read)
|
||||
pub fn read_messages<F1, F2>(
|
||||
&mut self,
|
||||
mut signature: F1,
|
||||
mut callback: F2,
|
||||
) -> NixResult<Result<usize, MessageParseError>>
|
||||
where
|
||||
F1: FnMut(u32, u16) -> Option<&'static [ArgumentType]>,
|
||||
F2: FnMut(Message) -> bool,
|
||||
{
|
||||
// message parsing
|
||||
let mut dispatched = 0;
|
||||
|
||||
loop {
|
||||
let mut err = None;
|
||||
// first parse any leftover messages
|
||||
loop {
|
||||
match self.read_one_message(&mut signature) {
|
||||
Ok(msg) => {
|
||||
let keep_going = callback(msg);
|
||||
dispatched += 1;
|
||||
if !keep_going {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
err = Some(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy back any leftover content to the front of the buffer
|
||||
self.in_data.move_to_front();
|
||||
self.in_fds.move_to_front();
|
||||
|
||||
if let Some(MessageParseError::Malformed) = err {
|
||||
// early stop here
|
||||
return Ok(Err(MessageParseError::Malformed));
|
||||
}
|
||||
|
||||
if err.is_none() && self.in_data.has_content() {
|
||||
// we stopped reading without error while there is content? That means
|
||||
// the user requested an early stopping
|
||||
return Ok(Ok(dispatched));
|
||||
}
|
||||
|
||||
// now, try to get more data
|
||||
match self.fill_incoming_buffers() {
|
||||
Ok(()) => (),
|
||||
Err(e @ ::nix::Error::EAGAIN) => {
|
||||
// stop looping, returning Ok() or EAGAIN depending on whether messages
|
||||
// were dispatched
|
||||
if dispatched == 0 {
|
||||
return Err(e);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Ok(dispatched))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer
|
||||
*/
|
||||
#[derive(Debug)]
|
||||
struct Buffer<T: Copy> {
|
||||
storage: Vec<T>,
|
||||
occupied: usize,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<T: Copy + Default> Buffer<T> {
|
||||
fn new(size: usize) -> Buffer<T> {
|
||||
Buffer { storage: vec![T::default(); size], occupied: 0, offset: 0 }
|
||||
}
|
||||
|
||||
/// Check if this buffer has content to read
|
||||
fn has_content(&self) -> bool {
|
||||
self.occupied > self.offset
|
||||
}
|
||||
|
||||
/// Advance the internal counter of occupied space
|
||||
fn advance(&mut self, bytes: usize) {
|
||||
self.occupied += bytes;
|
||||
}
|
||||
|
||||
/// Advance the read offset of current occupied space
|
||||
fn offset(&mut self, bytes: usize) {
|
||||
self.offset += bytes;
|
||||
}
|
||||
|
||||
/// Clears the contents of the buffer
|
||||
///
|
||||
/// This only sets the counter of occupied space back to zero,
|
||||
/// allowing previous content to be overwritten.
|
||||
fn clear(&mut self) {
|
||||
self.occupied = 0;
|
||||
self.offset = 0;
|
||||
}
|
||||
|
||||
/// Get the current contents of the occupied space of the buffer
|
||||
fn get_contents(&self) -> &[T] {
|
||||
&self.storage[(self.offset)..(self.occupied)]
|
||||
}
|
||||
|
||||
/// Get mutable access to the unoccupied space of the buffer
|
||||
fn get_writable_storage(&mut self) -> &mut [T] {
|
||||
&mut self.storage[(self.occupied)..]
|
||||
}
|
||||
|
||||
/// Move the unread contents of the buffer to the front, to ensure
|
||||
/// maximal write space availability
|
||||
fn move_to_front(&mut self) {
|
||||
unsafe {
|
||||
::std::ptr::copy(
|
||||
&self.storage[self.offset] as *const T,
|
||||
&mut self.storage[0] as *mut T,
|
||||
self.occupied - self.offset,
|
||||
);
|
||||
}
|
||||
self.occupied -= self.offset;
|
||||
self.offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::wire::{Argument, ArgumentType, Message};
|
||||
|
||||
use std::ffi::CString;
|
||||
|
||||
use smallvec::smallvec;
|
||||
|
||||
fn same_file(a: RawFd, b: RawFd) -> bool {
|
||||
let stat1 = ::nix::sys::stat::fstat(a).unwrap();
|
||||
let stat2 = ::nix::sys::stat::fstat(b).unwrap();
|
||||
stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino
|
||||
}
|
||||
|
||||
// check if two messages are equal
|
||||
//
|
||||
// if arguments contain FDs, check that the fd point to
|
||||
// the same file, rather than are the same number.
|
||||
fn assert_eq_msgs(msg1: &Message, msg2: &Message) {
|
||||
assert_eq!(msg1.sender_id, msg2.sender_id);
|
||||
assert_eq!(msg1.opcode, msg2.opcode);
|
||||
assert_eq!(msg1.args.len(), msg2.args.len());
|
||||
for (arg1, arg2) in msg1.args.iter().zip(msg2.args.iter()) {
|
||||
if let (&Argument::Fd(fd1), &Argument::Fd(fd2)) = (arg1, arg2) {
|
||||
assert!(same_file(fd1, fd2));
|
||||
} else {
|
||||
assert_eq!(arg1, arg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_read_cycle() {
|
||||
let msg = Message {
|
||||
sender_id: 42,
|
||||
opcode: 7,
|
||||
args: smallvec![
|
||||
Argument::Uint(3),
|
||||
Argument::Fixed(-89),
|
||||
Argument::Str(Box::new(CString::new(&b"I like trains!"[..]).unwrap())),
|
||||
Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
|
||||
Argument::Object(88),
|
||||
Argument::NewId(56),
|
||||
Argument::Int(-25),
|
||||
],
|
||||
};
|
||||
|
||||
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
|
||||
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
|
||||
|
||||
client.write_message(&msg).unwrap();
|
||||
client.flush().unwrap();
|
||||
|
||||
static SIGNATURE: &'static [ArgumentType] = &[
|
||||
ArgumentType::Uint,
|
||||
ArgumentType::Fixed,
|
||||
ArgumentType::Str,
|
||||
ArgumentType::Array,
|
||||
ArgumentType::Object,
|
||||
ArgumentType::NewId,
|
||||
ArgumentType::Int,
|
||||
];
|
||||
|
||||
let ret = server
|
||||
.read_messages(
|
||||
|sender_id, opcode| {
|
||||
if sender_id == 42 && opcode == 7 {
|
||||
Some(SIGNATURE)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|message| {
|
||||
assert_eq_msgs(&message, &msg);
|
||||
true
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ret, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_read_cycle_fd() {
|
||||
let msg = Message {
|
||||
sender_id: 42,
|
||||
opcode: 7,
|
||||
args: smallvec![
|
||||
Argument::Fd(1), // stdin
|
||||
Argument::Fd(0), // stdout
|
||||
],
|
||||
};
|
||||
|
||||
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
|
||||
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
|
||||
|
||||
client.write_message(&msg).unwrap();
|
||||
client.flush().unwrap();
|
||||
|
||||
static SIGNATURE: &'static [ArgumentType] = &[ArgumentType::Fd, ArgumentType::Fd];
|
||||
|
||||
let ret = server
|
||||
.read_messages(
|
||||
|sender_id, opcode| {
|
||||
if sender_id == 42 && opcode == 7 {
|
||||
Some(SIGNATURE)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|message| {
|
||||
assert_eq_msgs(&message, &msg);
|
||||
true
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ret, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_read_cycle_multiple() {
|
||||
let messages = [
|
||||
Message {
|
||||
sender_id: 42,
|
||||
opcode: 0,
|
||||
args: smallvec![
|
||||
Argument::Int(42),
|
||||
Argument::Str(Box::new(CString::new(&b"I like trains"[..]).unwrap())),
|
||||
],
|
||||
},
|
||||
Message {
|
||||
sender_id: 42,
|
||||
opcode: 1,
|
||||
args: smallvec![
|
||||
Argument::Fd(1), // stdin
|
||||
Argument::Fd(0), // stdout
|
||||
],
|
||||
},
|
||||
Message {
|
||||
sender_id: 42,
|
||||
opcode: 2,
|
||||
args: smallvec![
|
||||
Argument::Uint(3),
|
||||
Argument::Fd(2), // stderr
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
static SIGNATURES: &'static [&'static [ArgumentType]] = &[
|
||||
&[ArgumentType::Int, ArgumentType::Str],
|
||||
&[ArgumentType::Fd, ArgumentType::Fd],
|
||||
&[ArgumentType::Uint, ArgumentType::Fd],
|
||||
];
|
||||
|
||||
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
|
||||
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
|
||||
|
||||
for msg in &messages {
|
||||
client.write_message(msg).unwrap();
|
||||
}
|
||||
client.flush().unwrap();
|
||||
|
||||
let mut recv_msgs = Vec::new();
|
||||
let ret = server
|
||||
.read_messages(
|
||||
|sender_id, opcode| {
|
||||
if sender_id == 42 {
|
||||
Some(SIGNATURES[opcode as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|message| {
|
||||
recv_msgs.push(message);
|
||||
true
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ret, 3);
|
||||
assert_eq!(recv_msgs.len(), 3);
|
||||
for (msg1, msg2) in messages.iter().zip(recv_msgs.iter()) {
|
||||
assert_eq_msgs(msg1, msg2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_with_string_len_multiple_of_4() {
|
||||
let msg = Message {
|
||||
sender_id: 2,
|
||||
opcode: 0,
|
||||
args: smallvec![
|
||||
Argument::Uint(18),
|
||||
Argument::Str(Box::new(CString::new(&b"wl_shell"[..]).unwrap())),
|
||||
Argument::Uint(1),
|
||||
],
|
||||
};
|
||||
|
||||
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
|
||||
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
|
||||
|
||||
client.write_message(&msg).unwrap();
|
||||
client.flush().unwrap();
|
||||
|
||||
static SIGNATURE: &'static [ArgumentType] =
|
||||
&[ArgumentType::Uint, ArgumentType::Str, ArgumentType::Uint];
|
||||
|
||||
let ret = server
|
||||
.read_messages(
|
||||
|sender_id, opcode| {
|
||||
if sender_id == 2 && opcode == 0 {
|
||||
Some(SIGNATURE)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|message| {
|
||||
assert_eq_msgs(&message, &msg);
|
||||
true
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ret, 1);
|
||||
}
|
||||
}
|
||||
325
third-party/vendor/wayland-commons/src/user_data.rs
vendored
Normal file
325
third-party/vendor/wayland-commons/src/user_data.rs
vendored
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
//! Various utilities used for other implementations
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use std::any::Any;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::thread::{self, ThreadId};
|
||||
|
||||
use self::list::AppendList;
|
||||
|
||||
/// A wrapper for user data, able to store any type, and correctly
|
||||
/// handling access from a wrong thread
|
||||
#[derive(Debug)]
|
||||
pub struct UserData {
|
||||
inner: OnceCell<UserDataInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum UserDataInner {
|
||||
ThreadSafe(Box<dyn Any + Send + Sync + 'static>),
|
||||
NonThreadSafe(Box<ManuallyDrop<dyn Any + 'static>>, ThreadId),
|
||||
}
|
||||
|
||||
// UserData itself is always threadsafe, as it only gives access to its
|
||||
// content if it is send+sync or we are on the right thread
|
||||
unsafe impl Send for UserData {}
|
||||
unsafe impl Sync for UserData {}
|
||||
|
||||
impl UserData {
|
||||
/// Create a new UserData instance
|
||||
pub const fn new() -> UserData {
|
||||
UserData { inner: OnceCell::new() }
|
||||
}
|
||||
|
||||
/// Sets the UserData to a given value
|
||||
///
|
||||
/// The provided closure is called to init the UserData,
|
||||
/// does nothing is the UserData had already been set.
|
||||
pub fn set<T: Any + 'static, F: FnOnce() -> T>(&self, f: F) {
|
||||
self.inner.get_or_init(|| {
|
||||
UserDataInner::NonThreadSafe(Box::new(ManuallyDrop::new(f())), thread::current().id())
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the UserData to a given threadsafe value
|
||||
///
|
||||
/// The provided closure is called to init the UserData,
|
||||
/// does nothing is the UserData had already been set.
|
||||
pub fn set_threadsafe<T: Any + Send + Sync + 'static, F: FnOnce() -> T>(&self, f: F) {
|
||||
self.inner.get_or_init(|| UserDataInner::ThreadSafe(Box::new(f())));
|
||||
}
|
||||
|
||||
/// Attempt to access the wrapped user data
|
||||
///
|
||||
/// Will return `None` if either:
|
||||
///
|
||||
/// - The requested type `T` does not match the type used for construction
|
||||
/// - This `UserData` has been created using the non-threadsafe variant and access
|
||||
/// is attempted from an other thread than the one it was created on
|
||||
pub fn get<T: 'static>(&self) -> Option<&T> {
|
||||
match self.inner.get() {
|
||||
Some(&UserDataInner::ThreadSafe(ref val)) => <dyn Any>::downcast_ref::<T>(&**val),
|
||||
Some(&UserDataInner::NonThreadSafe(ref val, threadid)) => {
|
||||
// only give access if we are on the right thread
|
||||
if threadid == thread::current().id() {
|
||||
<dyn Any>::downcast_ref::<T>(&***val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UserData {
|
||||
fn drop(&mut self) {
|
||||
// only drop non-Send user data if we are on the right thread, leak it otherwise
|
||||
if let Some(&mut UserDataInner::NonThreadSafe(ref mut val, threadid)) = self.inner.get_mut()
|
||||
{
|
||||
if threadid == thread::current().id() {
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut **val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A storage able to store several values of `UserData`
|
||||
/// of different types. It behaves similarly to a `TypeMap`.
|
||||
#[derive(Debug)]
|
||||
pub struct UserDataMap {
|
||||
list: AppendList<UserData>,
|
||||
}
|
||||
|
||||
impl UserDataMap {
|
||||
/// Create a new map
|
||||
pub fn new() -> UserDataMap {
|
||||
UserDataMap { list: AppendList::new() }
|
||||
}
|
||||
|
||||
/// Attempt to access the wrapped user data of a given type
|
||||
///
|
||||
/// Will return `None` if no value of type `T` is stored in this `UserDataMap`
|
||||
/// and accessible from this thread
|
||||
pub fn get<T: 'static>(&self) -> Option<&T> {
|
||||
for user_data in &self.list {
|
||||
if let Some(val) = user_data.get::<T>() {
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Insert a value in the map if it is not already there
|
||||
///
|
||||
/// This is the non-threadsafe variant, the type you insert don't have to be
|
||||
/// threadsafe, but they will not be visible from other threads (even if they are
|
||||
/// actually threadsafe).
|
||||
///
|
||||
/// If the value does not already exists, the closure is called to create it and
|
||||
/// this function returns `true`. If the value already exists, the closure is not
|
||||
/// called, and this function returns `false`.
|
||||
pub fn insert_if_missing<T: 'static, F: FnOnce() -> T>(&self, init: F) -> bool {
|
||||
if self.get::<T>().is_some() {
|
||||
return false;
|
||||
}
|
||||
let data = UserData::new();
|
||||
data.set(init);
|
||||
self.list.append(data);
|
||||
true
|
||||
}
|
||||
|
||||
/// Insert a value in the map if it is not already there
|
||||
///
|
||||
/// This is the threadsafe variant, the type you insert must be threadsafe and will
|
||||
/// be visible from all threads.
|
||||
///
|
||||
/// If the value does not already exists, the closure is called to create it and
|
||||
/// this function returns `true`. If the value already exists, the closure is not
|
||||
/// called, and this function returns `false`.
|
||||
pub fn insert_if_missing_threadsafe<T: Send + Sync + 'static, F: FnOnce() -> T>(
|
||||
&self,
|
||||
init: F,
|
||||
) -> bool {
|
||||
if self.get::<T>().is_some() {
|
||||
return false;
|
||||
}
|
||||
let data = UserData::new();
|
||||
data.set_threadsafe(init);
|
||||
self.list.append(data);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UserDataMap {
|
||||
fn default() -> UserDataMap {
|
||||
UserDataMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
mod list {
|
||||
/*
|
||||
* This is a lock-free append-only list, it is used as an implementation
|
||||
* detail of the UserDataMap.
|
||||
*
|
||||
* It was extracted from https://github.com/Diggsey/lockless under MIT license
|
||||
* Copyright © Diggory Blake <diggsey@googlemail.com>
|
||||
*/
|
||||
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::{mem, ptr};
|
||||
|
||||
type NodePtr<T> = Option<Box<Node<T>>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Node<T> {
|
||||
value: T,
|
||||
next: AppendList<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppendList<T>(AtomicPtr<Node<T>>);
|
||||
|
||||
impl<T> AppendList<T> {
|
||||
fn node_into_raw(ptr: NodePtr<T>) -> *mut Node<T> {
|
||||
match ptr {
|
||||
Some(b) => Box::into_raw(b),
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
unsafe fn node_from_raw(ptr: *mut Node<T>) -> NodePtr<T> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Box::from_raw(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_internal(ptr: NodePtr<T>) -> Self {
|
||||
AppendList(AtomicPtr::new(Self::node_into_raw(ptr)))
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self::new_internal(None)
|
||||
}
|
||||
|
||||
pub fn append(&self, value: T) {
|
||||
self.append_list(AppendList::new_internal(Some(Box::new(Node {
|
||||
value,
|
||||
next: AppendList::new(),
|
||||
}))));
|
||||
}
|
||||
|
||||
unsafe fn append_ptr(&self, p: *mut Node<T>) {
|
||||
loop {
|
||||
match self.0.compare_exchange_weak(
|
||||
ptr::null_mut(),
|
||||
p,
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_) => return,
|
||||
Err(head) => {
|
||||
if !head.is_null() {
|
||||
return (*head).next.append_ptr(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_list(&self, other: AppendList<T>) {
|
||||
let p = other.0.load(Ordering::Acquire);
|
||||
mem::forget(other);
|
||||
unsafe { self.append_ptr(p) };
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> AppendListIterator<T> {
|
||||
AppendListIterator(&self.0)
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> AppendListMutIterator<T> {
|
||||
AppendListMutIterator(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a AppendList<T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = AppendListIterator<'a, T>;
|
||||
|
||||
fn into_iter(self) -> AppendListIterator<'a, T> {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a mut AppendList<T> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = AppendListMutIterator<'a, T>;
|
||||
|
||||
fn into_iter(self) -> AppendListMutIterator<'a, T> {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for AppendList<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { Self::node_from_raw(mem::replace(self.0.get_mut(), ptr::null_mut())) };
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppendListIterator<'a, T: 'a>(&'a AtomicPtr<Node<T>>);
|
||||
|
||||
impl<'a, T: 'a> Iterator for AppendListIterator<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
let p = self.0.load(Ordering::Acquire);
|
||||
if p.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
self.0 = &(*p).next.0;
|
||||
Some(&(*p).value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppendListMutIterator<'a, T: 'a>(&'a mut AtomicPtr<Node<T>>);
|
||||
|
||||
impl<'a, T: 'a> Iterator for AppendListMutIterator<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<&'a mut T> {
|
||||
let p = self.0.load(Ordering::Acquire);
|
||||
if p.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
self.0 = &mut (*p).next.0;
|
||||
Some(&mut (*p).value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::UserDataMap;
|
||||
|
||||
#[test]
|
||||
fn insert_twice() {
|
||||
let map = UserDataMap::new();
|
||||
|
||||
assert_eq!(map.get::<usize>(), None);
|
||||
assert!(map.insert_if_missing(|| 42usize));
|
||||
assert!(!map.insert_if_missing(|| 43usize));
|
||||
assert_eq!(map.get::<usize>(), Some(&42));
|
||||
}
|
||||
}
|
||||
460
third-party/vendor/wayland-commons/src/wire.rs
vendored
Normal file
460
third-party/vendor/wayland-commons/src/wire.rs
vendored
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
//! Types and routines used to manipulate arguments from the wire format
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
use nix::{Error as NixError, Result as NixResult};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
// The value of 4 is chosen for the following reasons:
|
||||
// - almost all messages have 4 arguments or less
|
||||
// - there are some potentially spammy events that have 3/4 arguments (wl_touch.move has 4 for example)
|
||||
//
|
||||
// This brings the size of Message to 11*usize (instead of 4*usize with a regular vec), but eliminates
|
||||
// almost all allocations that may occur during the processing of messages, both client-side and server-side.
|
||||
const INLINE_ARGS: usize = 4;
|
||||
|
||||
/// 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 destructor: bool,
|
||||
}
|
||||
|
||||
/// Enum of possible argument types as recognized by the wire
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum ArgumentType {
|
||||
/// i32
|
||||
Int,
|
||||
/// u32
|
||||
Uint,
|
||||
/// fixed point, 1/256 precision
|
||||
Fixed,
|
||||
/// CString
|
||||
Str,
|
||||
/// id of a wayland object
|
||||
Object,
|
||||
/// id of a newly created wayland object
|
||||
NewId,
|
||||
/// Vec<u8>
|
||||
Array,
|
||||
/// RawFd
|
||||
Fd,
|
||||
}
|
||||
|
||||
/// Enum of possible argument as recognized by the wire, including values
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[allow(clippy::box_collection)]
|
||||
pub enum Argument {
|
||||
/// i32
|
||||
Int(i32),
|
||||
/// u32
|
||||
Uint(u32),
|
||||
/// fixed point, 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(Box<CString>),
|
||||
/// id of a wayland object
|
||||
Object(u32),
|
||||
/// id of a newly created wayland object
|
||||
NewId(u32),
|
||||
/// 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>>),
|
||||
/// RawFd
|
||||
Fd(RawFd),
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
/// Retrieve the type of a given argument instance
|
||||
pub fn get_type(&self) -> ArgumentType {
|
||||
match *self {
|
||||
Argument::Int(_) => ArgumentType::Int,
|
||||
Argument::Uint(_) => ArgumentType::Uint,
|
||||
Argument::Fixed(_) => ArgumentType::Fixed,
|
||||
Argument::Str(_) => ArgumentType::Str,
|
||||
Argument::Object(_) => ArgumentType::Object,
|
||||
Argument::NewId(_) => ArgumentType::NewId,
|
||||
Argument::Array(_) => ArgumentType::Array,
|
||||
Argument::Fd(_) => ArgumentType::Fd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Argument {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Argument::Int(value) => write!(f, "{}", value),
|
||||
Argument::Uint(value) => write!(f, "{}", value),
|
||||
Argument::Fixed(value) => write!(f, "{}", value),
|
||||
Argument::Str(value) => write!(f, "{:?}", value),
|
||||
Argument::Object(value) => write!(f, "{}", value),
|
||||
Argument::NewId(value) => write!(f, "{}", value),
|
||||
Argument::Array(value) => write!(f, "{:?}", value),
|
||||
Argument::Fd(value) => write!(f, "{}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wire message
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Message {
|
||||
/// ID of the object sending this message
|
||||
pub sender_id: u32,
|
||||
/// Opcode of the message
|
||||
pub opcode: u16,
|
||||
/// Arguments of the message
|
||||
pub args: SmallVec<[Argument; INLINE_ARGS]>,
|
||||
}
|
||||
|
||||
/// Error generated when trying to serialize a message into buffers
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MessageWriteError {
|
||||
/// The buffer is too small to hold the message contents
|
||||
BufferTooSmall,
|
||||
/// The message contains a FD that could not be dup-ed
|
||||
DupFdFailed(::nix::Error),
|
||||
}
|
||||
|
||||
impl std::error::Error for MessageWriteError {}
|
||||
|
||||
impl std::fmt::Display for MessageWriteError {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
match *self {
|
||||
MessageWriteError::BufferTooSmall => {
|
||||
f.write_str("The provided buffer is too small to hold message content.")
|
||||
}
|
||||
MessageWriteError::DupFdFailed(_) => {
|
||||
f.write_str("The message contains a file descriptor that could not be dup()-ed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error generated when trying to deserialize a message from buffers
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MessageParseError {
|
||||
/// The message references a FD but the buffer FD is empty
|
||||
MissingFD,
|
||||
/// More data is needed to deserialize the message
|
||||
MissingData,
|
||||
/// The message is malformed and cannot be parsed
|
||||
Malformed,
|
||||
}
|
||||
|
||||
impl std::error::Error for MessageParseError {}
|
||||
|
||||
impl std::fmt::Display for MessageParseError {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
match *self {
|
||||
MessageParseError::MissingFD => {
|
||||
f.write_str("The message references a FD but the buffer FD is empty.")
|
||||
}
|
||||
MessageParseError::MissingData => {
|
||||
f.write_str("More data is needed to deserialize the message")
|
||||
}
|
||||
MessageParseError::Malformed => {
|
||||
f.write_str("The message is malformed and cannot be parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Serialize the contents of this message into provided buffers
|
||||
///
|
||||
/// Returns the number of elements written in each buffer
|
||||
///
|
||||
/// Any serialized Fd will be `dup()`-ed in the process
|
||||
pub fn write_to_buffers(
|
||||
&self,
|
||||
payload: &mut [u32],
|
||||
mut fds: &mut [RawFd],
|
||||
) -> Result<(usize, usize), MessageWriteError> {
|
||||
let orig_payload_len = payload.len();
|
||||
let orig_fds_len = fds.len();
|
||||
// Helper function to write a u32 or a RawFd to its buffer
|
||||
fn write_buf<T>(u: T, payload: &mut [T]) -> Result<&mut [T], MessageWriteError> {
|
||||
if let Some((head, tail)) = payload.split_first_mut() {
|
||||
*head = u;
|
||||
Ok(tail)
|
||||
} else {
|
||||
Err(MessageWriteError::BufferTooSmall)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to write byte arrays in payload
|
||||
fn write_array_to_payload<'a>(
|
||||
array: &[u8],
|
||||
payload: &'a mut [u32],
|
||||
) -> Result<&'a mut [u32], MessageWriteError> {
|
||||
let array_len = array.len();
|
||||
let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
|
||||
// need enough space to store the whole array with padding and a size header
|
||||
if payload.len() < 1 + word_len {
|
||||
return Err(MessageWriteError::BufferTooSmall);
|
||||
}
|
||||
// size header
|
||||
payload[0] = array_len as u32;
|
||||
let (buffer_slice, rest) = payload[1..].split_at_mut(word_len);
|
||||
unsafe {
|
||||
ptr::copy(array.as_ptr(), buffer_slice.as_mut_ptr() as *mut u8, array_len);
|
||||
}
|
||||
Ok(rest)
|
||||
}
|
||||
|
||||
let free_size = payload.len();
|
||||
if free_size < 2 {
|
||||
return Err(MessageWriteError::BufferTooSmall);
|
||||
}
|
||||
|
||||
let (header, mut payload) = payload.split_at_mut(2);
|
||||
|
||||
// we store all fds we dup-ed in this, which will auto-close
|
||||
// them on drop, if any of the `?` early-returns
|
||||
let mut pending_fds = FdStore::new();
|
||||
|
||||
// write the contents in the buffer
|
||||
for arg in &self.args {
|
||||
// Just to make the borrow checker happy
|
||||
let old_payload = payload;
|
||||
match *arg {
|
||||
Argument::Int(i) => payload = write_buf(i as u32, old_payload)?,
|
||||
Argument::Uint(u) => payload = write_buf(u, old_payload)?,
|
||||
Argument::Fixed(f) => payload = write_buf(f as u32, old_payload)?,
|
||||
Argument::Str(ref s) => {
|
||||
payload = write_array_to_payload(s.as_bytes_with_nul(), old_payload)?;
|
||||
}
|
||||
Argument::Object(o) => payload = write_buf(o, old_payload)?,
|
||||
Argument::NewId(n) => payload = write_buf(n, old_payload)?,
|
||||
Argument::Array(ref a) => {
|
||||
payload = write_array_to_payload(a, old_payload)?;
|
||||
}
|
||||
Argument::Fd(fd) => {
|
||||
let old_fds = fds;
|
||||
let dup_fd = dup_fd_cloexec(fd).map_err(MessageWriteError::DupFdFailed)?;
|
||||
pending_fds.push(dup_fd);
|
||||
fds = write_buf(dup_fd, old_fds)?;
|
||||
payload = old_payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we reached here, all writing was successful
|
||||
// no FD needs to be closed
|
||||
pending_fds.clear();
|
||||
|
||||
let wrote_size = (free_size - payload.len()) * 4;
|
||||
header[0] = self.sender_id;
|
||||
header[1] = ((wrote_size as u32) << 16) | u32::from(self.opcode);
|
||||
Ok((orig_payload_len - payload.len(), orig_fds_len - fds.len()))
|
||||
}
|
||||
|
||||
/// Attempts to parse a single wayland message with the given signature.
|
||||
///
|
||||
/// If the buffers contains several messages, only the first one will be parsed,
|
||||
/// and the unused tail of the buffers is returned. If a single message was present,
|
||||
/// the returned slices should thus be empty.
|
||||
///
|
||||
/// Errors if the message is malformed.
|
||||
pub fn from_raw<'a, 'b>(
|
||||
raw: &'a [u32],
|
||||
signature: &[ArgumentType],
|
||||
fds: &'b [RawFd],
|
||||
) -> Result<(Message, &'a [u32], &'b [RawFd]), MessageParseError> {
|
||||
// helper function to read arrays
|
||||
fn read_array_from_payload(
|
||||
array_len: usize,
|
||||
payload: &[u32],
|
||||
) -> Result<(&[u8], &[u32]), MessageParseError> {
|
||||
let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
|
||||
if word_len > payload.len() {
|
||||
return Err(MessageParseError::MissingData);
|
||||
}
|
||||
let (array_contents, rest) = payload.split_at(word_len);
|
||||
let array = unsafe {
|
||||
::std::slice::from_raw_parts(array_contents.as_ptr() as *const u8, array_len)
|
||||
};
|
||||
Ok((array, rest))
|
||||
}
|
||||
|
||||
if raw.len() < 2 {
|
||||
return Err(MessageParseError::MissingData);
|
||||
}
|
||||
|
||||
let sender_id = raw[0];
|
||||
let word_2 = raw[1];
|
||||
let opcode = (word_2 & 0x0000_FFFF) as u16;
|
||||
let len = (word_2 >> 16) as usize / 4;
|
||||
|
||||
if len < 2 || len > raw.len() {
|
||||
return Err(MessageParseError::Malformed);
|
||||
}
|
||||
|
||||
let (mut payload, rest) = raw.split_at(len);
|
||||
payload = &payload[2..];
|
||||
let mut fds = fds;
|
||||
|
||||
let arguments = signature
|
||||
.iter()
|
||||
.map(|argtype| {
|
||||
if let ArgumentType::Fd = *argtype {
|
||||
// don't consume input but fd
|
||||
if let Some((&front, tail)) = fds.split_first() {
|
||||
fds = tail;
|
||||
Ok(Argument::Fd(front))
|
||||
} else {
|
||||
Err(MessageParseError::MissingFD)
|
||||
}
|
||||
} else if let Some((&front, mut tail)) = payload.split_first() {
|
||||
let arg = match *argtype {
|
||||
ArgumentType::Int => Ok(Argument::Int(front as i32)),
|
||||
ArgumentType::Uint => Ok(Argument::Uint(front)),
|
||||
ArgumentType::Fixed => Ok(Argument::Fixed(front as i32)),
|
||||
ArgumentType::Str => read_array_from_payload(front as usize, tail)
|
||||
.and_then(|(v, rest)| {
|
||||
tail = rest;
|
||||
match CStr::from_bytes_with_nul(v) {
|
||||
Ok(s) => Ok(Argument::Str(Box::new(s.into()))),
|
||||
Err(_) => Err(MessageParseError::Malformed),
|
||||
}
|
||||
}),
|
||||
ArgumentType::Object => Ok(Argument::Object(front)),
|
||||
ArgumentType::NewId => Ok(Argument::NewId(front)),
|
||||
ArgumentType::Array => {
|
||||
read_array_from_payload(front as usize, tail).map(|(v, rest)| {
|
||||
tail = rest;
|
||||
Argument::Array(Box::new(v.into()))
|
||||
})
|
||||
}
|
||||
ArgumentType::Fd => unreachable!(),
|
||||
};
|
||||
payload = tail;
|
||||
arg
|
||||
} else {
|
||||
Err(MessageParseError::MissingData)
|
||||
}
|
||||
})
|
||||
.collect::<Result<SmallVec<_>, MessageParseError>>()?;
|
||||
|
||||
let msg = Message { sender_id, opcode, args: arguments };
|
||||
Ok((msg, rest, fds))
|
||||
}
|
||||
}
|
||||
|
||||
/// Duplicate a `RawFd` and set the CLOEXEC flag on the copy
|
||||
pub fn dup_fd_cloexec(fd: RawFd) -> NixResult<RawFd> {
|
||||
use nix::fcntl;
|
||||
match fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD_CLOEXEC(0)) {
|
||||
Ok(newfd) => Ok(newfd),
|
||||
Err(NixError::EINVAL) => {
|
||||
// F_DUPFD_CLOEXEC is not recognized, kernel too old, fallback
|
||||
// to setting CLOEXEC manually
|
||||
let newfd = fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD(0))?;
|
||||
|
||||
let flags = fcntl::fcntl(newfd, fcntl::FcntlArg::F_GETFD);
|
||||
let result = flags
|
||||
.map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
|
||||
.and_then(|f| fcntl::fcntl(newfd, fcntl::FcntlArg::F_SETFD(f)));
|
||||
match result {
|
||||
Ok(_) => {
|
||||
// setting the O_CLOEXEC worked
|
||||
Ok(newfd)
|
||||
}
|
||||
Err(e) => {
|
||||
// something went wrong in F_GETFD or F_SETFD
|
||||
let _ = ::nix::unistd::close(newfd);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* utility struct that closes every FD it contains on drop
|
||||
*/
|
||||
|
||||
struct FdStore {
|
||||
fds: Vec<RawFd>,
|
||||
}
|
||||
|
||||
impl FdStore {
|
||||
fn new() -> FdStore {
|
||||
FdStore { fds: Vec::new() }
|
||||
}
|
||||
fn push(&mut self, fd: RawFd) {
|
||||
self.fds.push(fd);
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
self.fds.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FdStore {
|
||||
fn drop(&mut self) {
|
||||
use nix::unistd::close;
|
||||
for fd in self.fds.drain(..) {
|
||||
// not much can be done if we can't close that anyway...
|
||||
let _ = close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use smallvec::smallvec;
|
||||
|
||||
#[test]
|
||||
fn into_from_raw_cycle() {
|
||||
let mut bytes_buffer = vec![0; 1024];
|
||||
let mut fd_buffer = vec![0; 10];
|
||||
|
||||
let msg = Message {
|
||||
sender_id: 42,
|
||||
opcode: 7,
|
||||
args: smallvec![
|
||||
Argument::Uint(3),
|
||||
Argument::Fixed(-89),
|
||||
Argument::Str(Box::new(CString::new(&b"I like trains!"[..]).unwrap())),
|
||||
Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
|
||||
Argument::Object(88),
|
||||
Argument::NewId(56),
|
||||
Argument::Int(-25),
|
||||
],
|
||||
};
|
||||
// write the message to the buffers
|
||||
msg.write_to_buffers(&mut bytes_buffer[..], &mut fd_buffer[..]).unwrap();
|
||||
// read them back
|
||||
let (rebuilt, _, _) = Message::from_raw(
|
||||
&bytes_buffer[..],
|
||||
&[
|
||||
ArgumentType::Uint,
|
||||
ArgumentType::Fixed,
|
||||
ArgumentType::Str,
|
||||
ArgumentType::Array,
|
||||
ArgumentType::Object,
|
||||
ArgumentType::NewId,
|
||||
ArgumentType::Int,
|
||||
],
|
||||
&fd_buffer[..],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(rebuilt, msg);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue