pub mod clm_matchers; use { crate::{ client::{Client, ClientId}, criteria::{ CritDestroyListener, CritLiteralOrRegex, CritMatcherId, CritMatcherIds, CritMgrExt, CritUpstreamNode, FixedRootMatcher, RootMatcherMap, clm::clm_matchers::{ clmm_is_xwayland::ClmMatchIsXwayland, clmm_pid::ClmMatchPid, clmm_sandboxed::ClmMatchSandboxed, clmm_string::{ ClmMatchSandboxAppId, ClmMatchSandboxEngine, ClmMatchSandboxInstanceId, }, clmm_uid::ClmMatchUid, }, crit_graph::{ CritMgr, CritRoot, CritRootFixed, CritTarget, CritTargetOwner, WeakCritTargetOwner, }, crit_leaf::{CritLeafEvent, CritLeafMatcher}, crit_matchers::critm_constant::CritMatchConstant, }, state::State, utils::{copyhashmap::CopyHashMap, hash_map_ext::HashMapExt, queue::AsyncQueue}, }, linearize::static_map, std::{ marker::PhantomData, rc::{Rc, Weak}, }, }; bitflags! { ClMatcherChange: u32; CL_CHANGED_DESTROYED = 1 << 0, CL_CHANGED_NEW = 1 << 1, } type ClmFixedRootMatcher = FixedRootMatcher, T>; pub struct ClMatcherManager { ids: Rc, changes: AsyncQueue>, leaf_events: Rc>>>, constant: ClmFixedRootMatcher>>, sandboxed: ClmFixedRootMatcher, is_xwayland: ClmFixedRootMatcher, matchers: Rc, } type ClmRootMatcherMap = RootMatcherMap, T>; #[derive(Default)] pub struct RootMatchers { sandbox_app_id: ClmRootMatcherMap, sandbox_engine: ClmRootMatcherMap, sandbox_instance_id: ClmRootMatcherMap, uid: ClmRootMatcherMap, pid: ClmRootMatcherMap, } pub async fn handle_cl_changes(state: Rc) { let mgr = &state.cl_matcher_manager; loop { let tl = mgr.changes.pop().await; mgr.update_matches(&tl); } } pub async fn handle_cl_leaf_events(state: Rc) { let mgr = &state.cl_matcher_manager; let debouncer = state.ring.debouncer(1000); loop { let event = mgr.leaf_events.pop().await; event.run(); debouncer.debounce().await; } } pub type ClmUpstreamNode = dyn CritUpstreamNode>; pub type ClmLeafMatcher = CritLeafMatcher>; impl ClMatcherManager { pub fn new(ids: &Rc) -> Self { let matchers = Rc::new(RootMatchers::default()); macro_rules! bool { ($name:ident) => {{ static_map! { v => CritRoot::new( &matchers, ids.next(), CritRootFixed($name(v), PhantomData), ) } }}; } Self { constant: CritMatchConstant::create(&matchers, ids), sandboxed: bool!(ClmMatchSandboxed), is_xwayland: bool!(ClmMatchIsXwayland), changes: Default::default(), leaf_events: Default::default(), ids: ids.clone(), matchers, } } pub fn clear(&self) { self.changes.clear(); self.leaf_events.clear(); } pub fn rematch_all(&self, state: &Rc) { for client in state.clients.clients.borrow().values() { client.data.property_changed(CL_CHANGED_NEW); } } pub fn changed(&self, client: &Rc) { self.changes.push(client.clone()); } fn update_matches(&self, data: &Rc) { let mut changed = data.changed_properties.take(); if changed.contains(CL_CHANGED_DESTROYED) { for destroyed in data.destroyed.lock().drain_values() { if let Some(destroyed) = destroyed.upgrade() { destroyed.destroyed(data.id); } } return; } macro_rules! handlers { ($name:ident) => { self.matchers .$name .lock() .values() .filter_map(|m| m.upgrade()) }; } macro_rules! fixed { ($name:ident) => { self.$name[false].handle(data); self.$name[true].handle(data); }; } if changed.contains(CL_CHANGED_NEW) { changed |= ClMatcherChange::all(); macro_rules! unconditional { ($field:ident) => { for m in handlers!($field) { m.handle(data); } }; } unconditional!(sandbox_instance_id); unconditional!(sandbox_app_id); unconditional!(sandbox_engine); unconditional!(uid); unconditional!(pid); fixed!(sandboxed); fixed!(is_xwayland); self.constant[true].handle(data); } } pub fn sandbox_engine(&self, string: CritLiteralOrRegex) -> Rc { self.root(ClmMatchSandboxEngine::new(string)) } pub fn sandbox_app_id(&self, string: CritLiteralOrRegex) -> Rc { self.root(ClmMatchSandboxAppId::new(string)) } pub fn sandbox_instance_id(&self, string: CritLiteralOrRegex) -> Rc { self.root(ClmMatchSandboxInstanceId::new(string)) } pub fn sandboxed(&self) -> Rc { self.sandboxed[true].clone() } pub fn uid(&self, pid: i32) -> Rc { self.root(ClmMatchUid(pid as _)) } pub fn pid(&self, pid: i32) -> Rc { self.root(ClmMatchPid(pid as _)) } pub fn is_xwayland(&self) -> Rc { self.is_xwayland[true].clone() } } impl CritTarget for Rc { type Id = ClientId; type Mgr = ClMatcherManager; type RootMatchers = RootMatchers; type LeafData = ClientId; type Owner = Weak; fn owner(&self) -> Self::Owner { Rc::downgrade(self) } fn id(&self) -> Self::Id { self.id } fn destroyed(&self) -> &CopyHashMap>> { &self.destroyed } fn leaf_data(&self) -> Self::LeafData { self.id } } impl CritTargetOwner for Rc { type Target = Rc; fn data(&self) -> &Self::Target { self } } impl WeakCritTargetOwner for Weak { type Target = Rc; type Owner = Rc; fn upgrade(&self) -> Option { self.upgrade() } } impl CritMgr for ClMatcherManager { type Target = Rc; fn id(&self) -> CritMatcherId { self.ids.next() } fn leaf_events(&self) -> &Rc>> { &self.leaf_events } fn match_constant(&self) -> &FixedRootMatcher> { &self.constant } fn roots(&self) -> &Rc<::RootMatchers> { &self.matchers } }