1
0
Fork 0
forked from wry/wry
wry/src/criteria/clm.rs
2026-03-13 18:13:41 +01:00

301 lines
8.6 KiB
Rust

pub mod clm_matchers;
use {
crate::{
client::{Client, ClientId},
criteria::{
CritDestroyListener, CritLiteralOrRegex, CritMatcherId, CritMatcherIds, CritMgrExt,
CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
clm::clm_matchers::{
clmm_id::ClmMatchId,
clmm_is_xwayland::ClmMatchIsXwayland,
clmm_pid::ClmMatchPid,
clmm_sandboxed::ClmMatchSandboxed,
clmm_string::{
ClmMatchComm, ClmMatchExe, ClmMatchSandboxAppId, ClmMatchSandboxEngine,
ClmMatchSandboxInstanceId, ClmMatchTag,
},
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,
CL_CHANGED_NEW,
}
type ClmFixedRootMatcher<T> = FixedRootMatcher<Rc<Client>, T>;
pub struct ClMatcherManager {
ids: Rc<CritMatcherIds>,
changes: AsyncQueue<Rc<Client>>,
leaf_events: Rc<AsyncQueue<CritLeafEvent<Rc<Client>>>>,
constant: ClmFixedRootMatcher<CritMatchConstant<Rc<Client>>>,
sandboxed: ClmFixedRootMatcher<ClmMatchSandboxed>,
is_xwayland: ClmFixedRootMatcher<ClmMatchIsXwayland>,
matchers: Rc<RootMatchers>,
}
type ClmRootMatcherMap<T> = RootMatcherMap<Rc<Client>, T>;
#[derive(Default)]
pub struct RootMatchers {
sandbox_app_id: ClmRootMatcherMap<ClmMatchSandboxAppId>,
sandbox_engine: ClmRootMatcherMap<ClmMatchSandboxEngine>,
sandbox_instance_id: ClmRootMatcherMap<ClmMatchSandboxInstanceId>,
uid: ClmRootMatcherMap<ClmMatchUid>,
pid: ClmRootMatcherMap<ClmMatchPid>,
comm: ClmRootMatcherMap<ClmMatchComm>,
exe: ClmRootMatcherMap<ClmMatchExe>,
tag: ClmRootMatcherMap<ClmMatchTag>,
id: ClmRootMatcherMap<ClmMatchId>,
}
impl RootMatchers {
fn clear(&self) {
self.sandbox_app_id.clear();
self.sandbox_engine.clear();
self.sandbox_instance_id.clear();
self.uid.clear();
self.pid.clear();
self.comm.clear();
self.exe.clear();
self.tag.clear();
self.id.clear();
}
}
pub async fn handle_cl_changes(state: Rc<State>) {
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<State>) {
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<Rc<Client>>;
pub type ClmLeafMatcher = CritLeafMatcher<Rc<Client>>;
impl ClMatcherManager {
pub fn new(ids: &Rc<CritMatcherIds>) -> 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();
self.constant.values().for_each(|c| c.clear());
self.sandboxed.values().for_each(|c| c.clear());
self.is_xwayland.values().for_each(|c| c.clear());
self.matchers.clear();
}
pub fn rematch_all(&self, state: &Rc<State>) {
for client in state.clients.clients.borrow().values() {
client.data.property_changed(CL_CHANGED_NEW);
}
}
pub fn changed(&self, client: &Rc<Client>) {
self.changes.push(client.clone());
}
fn update_matches(&self, data: &Rc<Client>) {
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);
unconditional!(comm);
unconditional!(exe);
unconditional!(tag);
unconditional!(id);
fixed!(sandboxed);
fixed!(is_xwayland);
self.constant[true].handle(data);
}
}
pub fn sandbox_engine(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchSandboxEngine::new(string))
}
pub fn sandbox_app_id(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchSandboxAppId::new(string))
}
pub fn sandbox_instance_id(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchSandboxInstanceId::new(string))
}
pub fn sandboxed(&self) -> Rc<ClmUpstreamNode> {
self.sandboxed[true].clone()
}
pub fn uid(&self, pid: i32) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchUid(pid as _))
}
pub fn pid(&self, pid: i32) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchPid(pid as _))
}
pub fn is_xwayland(&self) -> Rc<ClmUpstreamNode> {
self.is_xwayland[true].clone()
}
pub fn comm(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchComm::new(string))
}
pub fn exe(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchExe::new(string))
}
pub fn tag(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchTag::new(string))
}
pub fn id(&self, id: ClientId) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchId(id))
}
}
impl CritTarget for Rc<Client> {
type Id = ClientId;
type Mgr = ClMatcherManager;
type RootMatchers = RootMatchers;
type LeafData = ClientId;
type Owner = Weak<Client>;
fn owner(&self) -> Self::Owner {
Rc::downgrade(self)
}
fn id(&self) -> Self::Id {
self.id
}
fn destroyed(&self) -> &CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Self>>> {
&self.destroyed
}
fn leaf_data(&self) -> Self::LeafData {
self.id
}
}
impl CritTargetOwner for Rc<Client> {
type Target = Rc<Client>;
fn data(&self) -> &Self::Target {
self
}
}
impl WeakCritTargetOwner for Weak<Client> {
type Target = Rc<Client>;
type Owner = Rc<Client>;
fn upgrade(&self) -> Option<Self::Owner> {
self.upgrade()
}
}
impl CritMgr for ClMatcherManager {
type Target = Rc<Client>;
fn id(&self) -> CritMatcherId {
self.ids.next()
}
fn leaf_events(&self) -> &Rc<AsyncQueue<CritLeafEvent<Self::Target>>> {
&self.leaf_events
}
fn match_constant(&self) -> &FixedRootMatcher<Self::Target, CritMatchConstant<Self::Target>> {
&self.constant
}
fn roots(&self) -> &Rc<<Self::Target as CritTarget>::RootMatchers> {
&self.matchers
}
}