//! Environment management utilities //! //! This module provide the tools to automatically bind the wayland global objects you need in your program. //! //! At the heart of this is the `environment!` macro, which allows you to signal the globals you need //! and a struct to manage them as they are signaled in the registry. //! //! ## Global handlers //! //! Wayland globals are split in two kinds, that we will call here "single" globals and "multi" globals. //! //! - "single" globals represent a capability of the server. They are generally signaled in the registry //! from the start and never removed. They are signaled a single time. Examples of these globals are //! `wl_compositor`, `wl_shm` or `xdg_wm_base`. //! - "multi" globals represent a resource that the server gives you access to. These globals can be //! created or removed during the run of the program, and may exist as more than one instance, each //! representing a different physical resource. Examples of such globals are `wl_output` or `wl_seat`. //! //! The objects you need to handle these globals must implement one the two traits //! [`GlobalHandler`](trait.GlobalHandler.html) or [`MultiGlobalHandler`](trait.MultiGlobalHandler.html), //! depending on the kind of globals it will handle. These objects are responsible for binding the globals //! from the registry, and assigning them to filters to receive their events as necessary. //! //! This module provides a generic implementation of the [`GlobalHandler`](trait.GlobalHandler.html) trait //! as [`SimpleGlobal`](struct.SimpleGlobal.html). It can manage "single" globals that do not generate //! events, and thus require no filter. //! //! ## the `environment!` macro //! //! This macro is at the core of this module. See its documentation for details about how to //! use it: [`environment!`](../macro.environment.html). You can alternatively use the //! [`default_environment!`](../macro.default_environment.html) macro to quickly setup things and bring //! in all SCTK modules. use std::io::Result; use std::rc::Rc; use std::{cell::RefCell, fmt}; use wayland_client::{ protocol::{wl_display, wl_registry}, Attached, DispatchData, EventQueue, GlobalEvent, GlobalManager, Interface, Proxy, }; /* * Traits definitions */ /// Required trait for implementing a handler for "single" globals pub trait GlobalHandler { /// This global was created and signaled in the registry with given id and version fn created( &mut self, registry: Attached, id: u32, version: u32, ddata: DispatchData, ); /// Access the global if it was signaled fn get(&self) -> Option>; } /// Required trait for implementing a handler for "multi" globals pub trait MultiGlobalHandler { /// A new instance of this global was created with given id and version fn created( &mut self, registry: Attached, id: u32, version: u32, ddata: DispatchData, ); /// The instance with given id was removed fn removed(&mut self, id: u32, ddata: DispatchData); /// Access all the currently existing instances fn get_all(&self) -> Vec>; } /* * General Environment */ /// A Wayland Environment /// /// This struct is generated by the `environment!` macro, see module-level documentation /// for more details about this. /// /// This is the central point for accessing globals for your Wayland app. Any global that has /// previously been declared in the `environment!` macro can be access from this type via the /// `get_global`, `required_global` and `get_all_globals` methods. /// /// This `Environment` is a handle that can be cloned. pub struct Environment { /// The underlying `GlobalManager`, if you need to do manual interaction with the /// registry. See `wayland-client` documentation for details. pub manager: GlobalManager, inner: Rc>, } impl Environment { /// Create new `Environment` /// /// This requires access to a `wl_display` attached to the `event_queue`. /// You also need to provide an instance of the inner environment type declared /// using the [`environment!`](../macro.environment.html) macro. /// /// If you instead used the [`default_environment!`](../macro.default_environment.html), then /// you need to initialize your `Environment` using the /// [`new_default_environment!`](../macro.new_default_environment.html) macro. /// /// `std::io::Error` could be returned if initial roundtrips to the server failed. /// /// If this call indefinitely blocks when doing initial roundtrips this can only be /// caused by server bugs. pub fn new( display: &Attached, queue: &mut EventQueue, env: E, ) -> Result> { let environment = Self::new_pending(display, env); // Fully initialize the environment. queue.sync_roundtrip(&mut (), |event, _, _| { panic!( "Encountered unhandled event during initial roundtrip ({}::{})", event.interface, event.name ); })?; queue.sync_roundtrip(&mut (), |event, _, _| { panic!( "Encountered unhandled event during initial roundtrip ({}::{})", event.interface, event.name ); })?; Ok(environment) } /// Create new pending `Environment` /// /// This requires access to a `wl_display` attached to an event queue (on which the main SCTK logic /// will be attached). You also need to provide an instance of the inner environment type declared /// using the [`environment!`](../macro.environment.html) macro. /// /// If you instead used the [`default_environment!`](../macro.default_environment.html), then you need /// to initialize your `Environment` using the /// [`new_default_environment!`](../macro.new_default_environment.html) macro. /// /// You should prefer to use `Environment::new`, unless you want to control initialization /// manually or you create additional environment meaning that the initialization may be fine /// with just `dispatch_pending` of the event queue, instead of two roundtrips to /// fully initialize environment. If you manually initialize your environment two sync /// roundtrips are required. pub fn new_pending(display: &Attached, env: E) -> Environment { let inner = Rc::new(RefCell::new(env)); let my_inner = inner.clone(); let my_cb = move |event, registry, ddata: DispatchData| { let mut inner = my_inner.borrow_mut(); inner.process_event(event, registry, ddata); }; let manager = GlobalManager::new_with_cb(display, my_cb); Self { manager, inner } } } impl Environment { /// Access a "single" global /// /// This method allows you to access any "single" global that has previously /// been declared in the `environment!` macro. It is forwarded to the `get()` /// method of the appropriate `GlobalHandler`. /// /// It returns `None` if the global has not (yet) been signaled by the registry. pub fn get_global(&self) -> Option> where E: GlobalHandler, { self.inner.borrow().get() } /// Access a "single" global or panic /// /// This method is similar to `get_global`, but will panic with a detailed error /// message if the requested global was not advertized by the server. pub fn require_global(&self) -> Attached where E: GlobalHandler, { match self.inner.borrow().get() { Some(g) => g, None => panic!("[SCTK] A missing global was required: {}", I::NAME), } } /// Access all instances of a "multi" global /// /// This will return a `Vec` containing all currently existing instances of the /// requested "multi" global that has been previously declared in the `environment!` /// macro. It is forwarded to the `get_all()` method of the appropriate /// `MultiGlobalHandler`. pub fn get_all_globals(&self) -> Vec> where E: MultiGlobalHandler, { self.inner.borrow().get_all() } /// Access the inner environment /// /// This gives your access, via a closure, to the inner type you declared /// via the [`environment!`](../macro.environment.html) or /// [`default_environment!`](../macro.default_environment.html) macro. /// /// This method returns the return value of your closure. pub fn with_inner T>(&self, f: F) -> T { let mut inner = self.inner.borrow_mut(); f(&mut *inner) } } impl Clone for Environment { fn clone(&self) -> Environment { Environment { manager: self.manager.clone(), inner: self.inner.clone() } } } impl fmt::Debug for Environment where E: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Environment") .field("manager", &self.manager) .field("inner", &self.inner) .finish() } } /// Internal trait for the `Environment` logic /// /// This trait is automatically implemented by the [`environment!`](../macro.environment.html) /// macro, you should not implement it manually unless you seriously want to. pub trait InnerEnv { /// Process a `GlobalEvent` fn process_event( &mut self, event: GlobalEvent, registry: Attached, data: DispatchData, ); } /* * Simple handlers */ /// A minimalist global handler for "single" globals /// /// This handler will simply register the global as soon as the registry signals /// it, and do nothing more. /// /// It is appropriate for globals that never generate events, like `wl_compositor` /// or `wl_data_device_manager`. #[derive(Debug)] pub struct SimpleGlobal { global: Option>, } impl SimpleGlobal { /// Create a new handler pub fn new() -> SimpleGlobal { SimpleGlobal { global: None } } } impl> + AsRef>> GlobalHandler for SimpleGlobal { fn created( &mut self, registry: Attached, id: u32, version: u32, _: DispatchData, ) { let version = I::VERSION.min(version); self.global = Some((*registry.bind::(version, id)).clone()) } fn get(&self) -> Option> { self.global.clone() } } /* * environment! macro */ /// Macro for declaring an environment /// /// It needs to be used in conjunction with a a `struct` you declared, which will serve as the inner /// environment and hold the handlers for your globals. /// /// The macro is invoked as such: /// /// ```no_run /// # extern crate smithay_client_toolkit as sctk; /// # use sctk::reexports::client::protocol::{wl_compositor::WlCompositor, wl_subcompositor::WlSubcompositor, wl_output::WlOutput}; /// # use sctk::environment::SimpleGlobal; /// # use sctk::environment; /// # use sctk::output::OutputHandler; /// struct MyEnv { /// compositor: SimpleGlobal, /// subcompositor: SimpleGlobal, /// outputs: OutputHandler /// } /// /// environment!(MyEnv, /// singles = [ /// WlCompositor => compositor, /// WlSubcompositor => subcompositor, /// ], /// multis = [ /// WlOutput => outputs, /// ] /// ); /// ``` /// /// This will define how your `MyEnv` struct is able to manage the `WlCompositor`, `WlSubcompositor` and /// `WlOutput` globals. For each global, you need to provide a pattern /// `$type => $name` where: /// /// - `$type` is the type (implementing the `Interface` trait from `wayland-client`) representing a global /// - `$name` is the name of the field of `MyEnv` that is in charge of managing this global, implementing the /// appropriate `GlobalHandler` or `MultiGlobalHandler` trait /// /// It is possible to route several globals to the same field as long as it implements all the appropriate traits. #[macro_export] macro_rules! environment { ($env_name:ident, singles = [$($sty:ty => $sname:ident),* $(,)?], multis = [$($mty:ty => $mname:ident),* $(,)?]$(,)? ) => { impl $crate::environment::InnerEnv for $env_name { fn process_event( &mut self, event: $crate::reexports::client::GlobalEvent, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, ddata: $crate::reexports::client::DispatchData, ) { match event { $crate::reexports::client::GlobalEvent::New { id, interface, version } => match &interface[..] { $( <$sty as $crate::reexports::client::Interface>::NAME => $crate::environment::GlobalHandler::<$sty>::created(&mut self.$sname, registry, id, version, ddata), )* $( <$mty as $crate::reexports::client::Interface>::NAME => $crate::environment::MultiGlobalHandler::<$mty>::created(&mut self.$mname, registry, id, version, ddata), )* _ => { /* ignore unkown globals */ } }, $crate::reexports::client::GlobalEvent::Removed { id, interface } => match &interface[..] { $( <$mty as $crate::reexports::client::Interface>::NAME => $crate::environment::MultiGlobalHandler::<$mty>::removed(&mut self.$mname, id, ddata), )* _ => { /* ignore unknown globals */ } } } } } $( impl $crate::environment::GlobalHandler<$sty> for $env_name { fn created(&mut self, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, id: u32, version: u32, ddata: $crate::reexports::client::DispatchData) { $crate::environment::GlobalHandler::<$sty>::created(&mut self.$sname, registry, id, version, ddata) } fn get(&self) -> Option<$crate::reexports::client::Attached<$sty>> { $crate::environment::GlobalHandler::<$sty>::get(&self.$sname) } } )* $( impl $crate::environment::MultiGlobalHandler<$mty> for $env_name { fn created(&mut self, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, id: u32, version: u32, ddata: $crate::reexports::client::DispatchData) { $crate::environment::MultiGlobalHandler::<$mty>::created(&mut self.$mname, registry, id, version, ddata) } fn removed(&mut self, id: u32, ddata: $crate::reexports::client::DispatchData) { $crate::environment::MultiGlobalHandler::<$mty>::removed(&mut self.$mname, id, ddata) } fn get_all(&self) -> Vec<$crate::reexports::client::Attached<$mty>> { $crate::environment::MultiGlobalHandler::<$mty>::get_all(&self.$mname) } } )* }; }