| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::mem; |
| |
| use failure::Error; |
| use fuchsia_wayland_core as wl; |
| |
| use crate::client::Client; |
| use crate::object::MessageReceiver; |
| |
| /// Helper for constructing a |Registry|. |
| /// |
| /// A proper registry implementation can support adding and removing |Global|s |
| /// at runtime. Since we do not yet support these features, we will only allow |
| /// |Global|s to be added at initialization time and then leave the |Registry| |
| /// immutable. |
| /// |
| /// Note: the |Registry| only works for single-threaded event loops. |
| /// |
| /// TODO(tjdetwiler): Allow interfaces to be added and removed from the |
| /// |Registry| dynamically. |
| pub struct RegistryBuilder { |
| globals: Vec<Global>, |
| } |
| |
| impl RegistryBuilder { |
| pub fn new() -> Self { |
| RegistryBuilder { |
| globals: Vec::new(), |
| } |
| } |
| |
| /// Adds a new global interface to the registry. |
| /// |
| /// The wayland interface name & version are identified by the |Interface| |
| /// provided. When a client attempts to bind to this global using |
| /// |wl_registry::bind|, the closure will be called to create a |
| /// |MessageReceiver| that will be use to handle requests to the instance |
| /// of that global. |
| pub fn add_global< |
| I: wl::Interface + 'static, |
| F: FnMut(wl::ObjectId, &mut Client) -> Result<Box<MessageReceiver>, Error> + Send + 'static, |
| >( |
| &mut self, _: I, bind: F, |
| ) -> &mut Self { |
| self.globals.push(Global { |
| name: I::NAME, |
| version: I::VERSION, |
| requests: &I::REQUESTS, |
| bind_fn: Box::new(bind), |
| }); |
| self |
| } |
| |
| pub fn build(&mut self) -> Registry { |
| Registry { |
| globals: mem::replace(&mut self.globals, vec![]), |
| } |
| } |
| } |
| |
| /// The |Registry| holds the global interfaces that are advertised to clients. |
| pub struct Registry { |
| globals: Vec<Global>, |
| } |
| |
| impl Registry { |
| pub fn globals(&mut self) -> &mut [Global] { |
| self.globals.as_mut_slice() |
| } |
| } |
| |
| pub struct Global { |
| name: &'static str, |
| version: u32, |
| requests: &'static wl::MessageGroupSpec, |
| bind_fn: Box<FnMut(wl::ObjectId, &mut Client) -> Result<Box<MessageReceiver>, Error> + Send>, |
| } |
| |
| impl Global { |
| /// The wayland interface name for this global. |
| pub fn interface(&self) -> &str { |
| self.name |
| } |
| |
| /// The wayland interface version for this global. |
| pub fn version(&self) -> u32 { |
| self.version |
| } |
| |
| /// A descriptor of the set of requests this global can handle. |
| pub fn request_spec(&self) -> &'static wl::MessageGroupSpec { |
| self.requests |
| } |
| |
| /// Create a new object instance for this global. The returned |
| /// |MessageReceiver| will be used to handle all requests for the new |
| /// object. |
| pub fn bind( |
| &mut self, id: wl::ObjectId, client: &mut Client, |
| ) -> Result<Box<MessageReceiver>, Error> { |
| (*self.bind_fn)(id, client) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| use std::sync::Arc; |
| |
| use crate::test_protocol::{TestInterface, TestInterface2}; |
| use failure::Error; |
| use fuchsia_async as fasync; |
| use fuchsia_wayland_core::{Interface, IntoMessage}; |
| use fuchsia_zircon as zx; |
| use parking_lot::Mutex; |
| |
| use crate::client::Client; |
| use crate::object::RequestDispatcher; |
| use crate::test_protocol::*; |
| |
| #[test] |
| fn registry_bind() -> Result<(), Error> { |
| // We'll pass this to our globals and verify that the right number of |
| // messages are delivered. |
| #[derive(Default)] |
| struct Counts { |
| interface_1_bind_count: usize, |
| interface_2_bind_count: usize, |
| } |
| let counts: Arc<Mutex<Counts>> = Arc::new(Mutex::new(Default::default())); |
| let mut registry = RegistryBuilder::new(); |
| |
| { |
| let counts = counts.clone(); |
| registry.add_global(TestInterface, move |_, _| { |
| counts.lock().interface_1_bind_count += 1; |
| Ok(Box::new(RequestDispatcher::new(TestReceiver::new()))) |
| }); |
| } |
| { |
| let counts = counts.clone(); |
| registry.add_global(TestInterface2, move |_, _| { |
| counts.lock().interface_2_bind_count += 1; |
| Ok(Box::new(RequestDispatcher::new(TestReceiver::new()))) |
| }); |
| } |
| |
| // Build the registry & verify initial counts. |
| let registry = registry.build(); |
| assert_eq!(0, counts.lock().interface_1_bind_count); |
| assert_eq!(0, counts.lock().interface_2_bind_count); |
| |
| // Bind to the globals. |
| let (c1, _c2) = zx::Channel::create()?; |
| let registry = Arc::new(Mutex::new(registry)); |
| let _executor = fasync::Executor::new(); |
| let mut client = Client::new(fasync::Channel::from_channel(c1)?, registry.clone()); |
| |
| let receivers: Vec<Box<MessageReceiver>> = registry |
| .lock() |
| .globals |
| .iter_mut() |
| .map(|g| g.bind(0, &mut client).unwrap()) |
| .collect(); |
| for (id, r) in receivers.into_iter().enumerate() { |
| client.add_object_raw(id as u32, r, &TestInterface::REQUESTS)?; |
| } |
| assert_eq!(1, counts.lock().interface_1_bind_count); |
| assert_eq!(1, counts.lock().interface_2_bind_count); |
| assert_eq!(0, client.get_object::<TestReceiver>(0)?.count()); |
| assert_eq!(0, client.get_object::<TestReceiver>(1)?.count()); |
| |
| // Dispatch a message to each receiver. |
| client.receive_message(TestMessage::Message1.into_message(0)?)?; |
| client.receive_message(TestMessage::Message1.into_message(1)?)?; |
| |
| assert_eq!(1, counts.lock().interface_1_bind_count); |
| assert_eq!(1, counts.lock().interface_2_bind_count); |
| assert_eq!(1, client.get_object::<TestReceiver>(0)?.count()); |
| assert_eq!(1, client.get_object::<TestReceiver>(1)?.count()); |
| Ok(()) |
| } |
| } |