use { crate::{ client::ClientError, ifs::head_management::{ Head, HeadState, jay_head_manager_session_v1::{JayHeadManagerSessionV1, JayHeadManagerSessionV1Error}, jay_head_manager_v1::JayHeadManagerV1, jay_head_v1::JayHeadV1, }, object::{ObjectId, Version}, state::ConnectorData, utils::clonecell::CloneCell, }, std::rc::Rc, }; macro_rules! error_ { ($error_name:ident, $($tt:tt)*) => { #[derive(Debug, thiserror::Error)] pub enum $error_name { #[error(transparent)] ClientError(Box), #[error(transparent)] Common(#[from] super::super::HeadCommonError), $($tt)* } efrom!($error_name, ClientError, crate::client::ClientError); }; } macro_rules! impl_head_ext { ( $snake:ident, $camel:ident, $macro_name:ident, $mgr_module:ident, $mgr_name:ident, $mgr_id_name:ident, $head_module:ident, $head_name:ident, $head_id_name:ident, $version:expr, $(filter = $filter:ident,)? $(after_announce = $after_announce:ident,)? $(after_transaction = $after_transaction:ident,)? ) => { pub(in super::super) struct $mgr_name { pub(in super::super) id: crate::wire::$mgr_id_name, pub(in super::super) client: std::rc::Rc, pub(in super::super) tracker: crate::leaks::Tracker, pub(in super::super) version: crate::object::Version, pub(in super::super) common: std::rc::Rc, } impl $mgr_name { pub(in super::super) const VERSION: crate::object::Version = crate::object::Version($version); pub(in super::super) const NAME: &str = concat!("jay_head_manager_ext_", stringify!($snake)); } object_base! { self = $mgr_name; version = self.version; } impl crate::object::Object for $mgr_name {} simple_add_obj!($mgr_name); pub(in super::super) struct $head_name { pub(in super::super) id: crate::wire::$head_id_name, pub(in super::super) client: std::rc::Rc, pub(in super::super) tracker: crate::leaks::Tracker, pub(in super::super) version: crate::object::Version, pub(in super::super) common: std::rc::Rc, } object_base! { self = $head_name; version = self.version; } impl crate::object::Object for $head_name {} simple_add_obj!($head_name); impl $mgr_name { pub(in super::super) fn announce( &self, #[allow(clippy::allow_attributes, unused_variables)] connector: &crate::state::ConnectorData, common: &std::rc::Rc, ) -> Result>, crate::client::ClientError> { $( if !self.$filter(connector, common) { return Ok(None); } )? let obj = std::rc::Rc::new($head_name { id: self.client.new_id()?, client: self.client.clone(), tracker: Default::default(), version: self.version, common: common.clone(), }); track!(self.client, obj); self.client.add_server_obj(&obj); self.send_head(&obj); Ok(Some(obj)) } fn send_head(&self, obj: &$head_name) { self.client.event(crate::wire::$mgr_module::Head { self_id: self.id, head: obj.id, }); } } impl $head_name { pub(in super::super) fn after_announce_wrapper( &self, #[allow(clippy::allow_attributes, unused_variables)] shared: &super::super::HeadState, ) { $( self.$after_announce(shared); )? } pub(in super::super) fn after_transaction_wrapper( &self, #[allow(clippy::allow_attributes, unused_variables)] shared: &super::super::HeadState, #[allow(clippy::allow_attributes, unused_variables)] tran: &super::super::HeadState, ) { $( self.$after_transaction(shared, tran); )? } } } } #[rustfmt::skip] macro_rules! declare_extension_type_macro { ( ($dollar:tt), $snake:ident, $camel:ident, $macro_name:ident, $macro_name_int:ident, $mgr_module:ident, $mgr_name:ident, $mgr_id_name:ident, $mgr_request_handler:ident, $head_module:ident, $head_name:ident, $head_id_name:ident, $head_request_handler:ident, $error_name:ident, ) => { macro_rules! $macro_name { ( version = $version:expr, $dollar(filter = $filter:ident,)? $dollar(after_announce = $after_announce:ident,)? $dollar(after_transaction = $after_transaction:ident,)? ) => { $macro_name_int! { ($), version = $version, $dollar(filter = $filter,)? $dollar(after_announce = $after_announce,)? $dollar(after_transaction = $after_transaction,)? } } } macro_rules! $macro_name_int { ( ($dollar2:tt), version = $version:expr, $dollar(filter = $filter:ident,)? $dollar(after_announce = $after_announce:ident,)? $dollar(after_transaction = $after_transaction:ident,)? ) => { impl_head_ext!( $snake, $camel, $macro_name, $mgr_module, $mgr_name, $mgr_id_name, $head_module, $head_name, $head_id_name, $version, $dollar(filter = $filter,)? $dollar(after_announce = $after_announce,)? $dollar(after_transaction = $after_transaction,)? ); macro_rules! head_common_req { () => { head_common_req_!($snake); } } macro_rules! mgr_common_req { () => { mgr_common_req_!($snake); } } type ErrorName = $error_name; type MgrName = $mgr_name; type HeadName = $head_name; macro_rules! error { ($dollar2($tt:tt)*) => { error_!($error_name, $dollar2($tt)*); } } } } }; } macro_rules! mgr_common_req_ { ($snake:ident) => { with_builtin_macros::with_eager_expansions! { fn destroy( &self, _req: crate::wire::#{concat_idents!(jay_head_manager_ext_, $snake)}::Destroy, _slf: &Rc, ) -> Result<(), Self::Error> { self.common.assert_stopped()?; self.client.remove_obj(self)?; Ok(()) } } }; } macro_rules! head_common_req_ { ($snake:ident) => { with_builtin_macros::with_eager_expansions! { fn destroy( &self, _req: crate::wire::#{concat_idents!(jay_head_ext_, $snake)}::Destroy, _slf: &Rc, ) -> Result<(), Self::Error> { self.common.assert_removed()?; self.client.remove_obj(self)?; Ok(()) } } }; } macro_rules! declare_extensions { ($($snake:ident: $camel:ident,)*) => { #[derive(linearize::Linearize)] pub(super) enum HeadExtension { $($camel,)* } pub(super) fn send_available_extensions(mgr: &JayHeadManagerV1) { use linearize::Linearize; with_builtin_macros::with_eager_expansions! { $( type $camel = super::jay_head_ext::#{concat_idents!(jay_head_ext_, $snake)}::#{concat_idents!(JayHeadManagerExt, $camel)}; mgr.send_extension( HeadExtension::$camel.linearize() as _, $camel::NAME, $camel::VERSION, ); )* } } pub(super) fn announce_head( session: &Rc, head: &Rc, connector: &ConnectorData, ) -> Result, ClientError> { session.send_head_start(head, connector.head_managers.name); let head = super::Head { session: session.clone(), common: head.common.clone(), head: head.clone(), ext: HeadExts { $( $snake: match session.ext.$snake.get() { Some(f) => f.announce(connector, &head.common)?, _ => None, }, )* }, }; session.send_head_complete(); let shared = &*connector.head_managers.state.borrow(); $( if let Some(ext) = &head.ext.$snake { ext.after_announce_wrapper(shared); } )* Ok(Rc::new(head)) } pub(super) fn bind_extension(session: &JayHeadManagerSessionV1, ext: HeadExtension, name: u32, version: u32, id: ObjectId) -> Result<(), JayHeadManagerSessionV1Error> { match ext { $( HeadExtension::$camel => { if session.ext.$snake.is_some() { return Err(JayHeadManagerSessionV1Error::AlreadyBound(name)); } let version = Version(version); with_builtin_macros::with_eager_expansions! { type T = super::jay_head_ext::#{concat_idents!(jay_head_ext_, $snake)}::#{concat_idents!(JayHeadManagerExt, $camel)}; if version > T::VERSION { return Err(JayHeadManagerSessionV1Error::UnsupportedVersion(name, version)); } let obj = Rc::new(T { id: id.into(), client: session.client.clone(), tracker: Default::default(), version, common: session.common.clone(), }); } track!(session.client, obj); session.client.add_client_obj(&obj)?; session.ext.$snake.set(Some(obj)); } )* } Ok(()) } with_builtin_macros::with_eager_expansions! { #[derive(Default)] pub(super) struct MgrExts { $( pub(super) $snake: CloneCell>>, )* } pub(super) struct HeadExts { $( pub(super) $snake: Option>, )* } } impl HeadExts { pub(super) fn after_transaction(&self, shared: &HeadState, tran: &HeadState) { $( if let Some(ext) = &self.$snake { ext.after_transaction_wrapper(shared, tran); } )* } } with_builtin_macros::with_eager_expansions! { $( declare_extension_type_macro!( ($), $snake, $camel, #{concat_idents!(impl_, $snake)}, #{concat_idents!(impl_, $snake, _int)}, #{concat_idents!(jay_head_manager_ext_, $snake)}, #{concat_idents!(JayHeadManagerExt, $camel)}, #{concat_idents!(JayHeadManagerExt, $camel, Id)}, #{concat_idents!(JayHeadManagerExt, $camel, RequestHandler)}, #{concat_idents!(jay_head_ext_, $snake)}, #{concat_idents!(JayHeadExt, $camel)}, #{concat_idents!(JayHeadExt, $camel, Id)}, #{concat_idents!(JayHeadExt, $camel, RequestHandler)}, #{concat_idents!(JayHeadExt, $camel, Error)}, ); )* } }; } declare_extensions! { core_info_v1: CoreInfoV1, compositor_space_info_v1: CompositorSpaceInfoV1, compositor_space_positioner_v1: CompositorSpacePositionerV1, compositor_space_transformer_v1: CompositorSpaceTransformerV1, compositor_space_scaler_v1: CompositorSpaceScalerV1, compositor_space_enabler_v1: CompositorSpaceEnablerV1, connector_info_v1: ConnectorInfoV1, mode_info_v1: ModeInfoV1, mode_setter_v1: ModeSetterV1, physical_display_info_v1: PhysicalDisplayInfoV1, non_desktop_info_v1: NonDesktopInfoV1, }