From f4fdf750eca0cef8a3d11b6280362166ce466577 Mon Sep 17 00:00:00 2001 From: kossLAN Date: Fri, 29 May 2026 18:47:25 -0400 Subject: [PATCH] config: split matcher handling --- src/config/handler.rs | 297 +------------------------------- src/config/handler/matchers.rs | 299 +++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+), 296 deletions(-) create mode 100644 src/config/handler/matchers.rs diff --git a/src/config/handler.rs b/src/config/handler.rs index 7edd0e48..21e3848e 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -88,6 +88,7 @@ use { }; mod dispatch; +mod matchers; pub(super) struct ConfigProxyHandler { pub client_data: Cell<*const u8>, @@ -2071,302 +2072,6 @@ impl ConfigProxyHandler { .ok_or(CphError::WindowDoesNotExist(window)) } - fn get_client_matcher( - &self, - matcher: ClientMatcher, - ) -> Result>, CphError> { - self.client_matchers - .get(&matcher) - .ok_or(CphError::ClientMatcherDoesNotExist(matcher)) - } - - fn sort_generic_matcher( - &self, - generic: &mut GenericCriterionPayload, - key: impl FnMut(&T) -> K, - ) where - K: Ord, - { - match generic { - GenericCriterionPayload::List { list, .. } | GenericCriterionPayload::Exactly { list, .. } => { - list.sort_by_key(key) - } - GenericCriterionPayload::Matcher(_) | GenericCriterionPayload::Not(_) => {} - } - } - - fn create_generic_matcher( - &self, - mgr: &Mgr, - generic: &GenericCriterionPayload, - upstream: &mut Vec>>, - get_matcher: impl Fn(&Matcher) -> Result>, CphError>, - ) -> Result>, CphError> - where - Crit: Clone + Hash + Eq, - Mgr: CritMgrExt, - { - let mut get_upstream = |m: &Matcher| -> Result<_, CphError> { - let m = get_matcher(m)?; - let node = m.node.clone(); - upstream.push(m); - Ok(node) - }; - let node = match generic { - GenericCriterionPayload::Matcher(m) => get_matcher(m)?.node.clone(), - GenericCriterionPayload::Not(m) => mgr.not(&get_upstream(m)?), - GenericCriterionPayload::List { list, all } => { - let mut m = Vec::with_capacity(list.len()); - for c in list { - m.push(get_upstream(c)?); - } - mgr.list(&m, *all) - } - GenericCriterionPayload::Exactly { list, num } => { - let mut m = Vec::with_capacity(list.len()); - for c in list { - m.push(get_upstream(c)?); - } - mgr.exactly(&m, *num) - } - }; - Ok(node) - } - - fn handle_create_client_matcher( - &self, - mut criterion: ClientCriterionPayload, - ) -> Result<(), CphError> { - if let ClientCriterionPayload::Generic(generic) = &mut criterion { - self.sort_generic_matcher(generic, |m| m.0); - } - let id = ClientMatcher(self.client_matcher_ids.fetch_add(1)); - let cache = &self.client_matcher_cache; - if let Some(matcher) = cache.get(&criterion) - && let Some(matcher) = matcher.upgrade() - { - self.client_matchers.set(id, matcher); - self.respond(Response::CreateClientMatcher { matcher: id }); - return Ok(()); - } - let mgr = &self.state.cl_matcher_manager; - let mut upstream = vec![]; - let matcher = match &criterion { - ClientCriterionPayload::Generic(m) => { - self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_client_matcher(*m))? - } - ClientCriterionPayload::String { - string, - field, - regex, - } => { - let needle = match *regex { - true => { - let regex = Regex::new(string).map_err(CphError::InvalidRegex)?; - CritLiteralOrRegex::Regex(regex) - } - false => CritLiteralOrRegex::Literal(string.to_string()), - }; - match *field { - ClientCriterionStringField::SandboxEngine => mgr.sandbox_engine(needle), - ClientCriterionStringField::SandboxAppId => mgr.sandbox_app_id(needle), - ClientCriterionStringField::SandboxInstanceId => { - mgr.sandbox_instance_id(needle) - } - ClientCriterionStringField::Comm => mgr.comm(needle), - ClientCriterionStringField::Exe => mgr.exe(needle), - } - } - ClientCriterionPayload::Sandboxed => mgr.sandboxed(), - ClientCriterionPayload::Uid(p) => mgr.uid(*p), - ClientCriterionPayload::Pid(p) => mgr.pid(*p), - ClientCriterionPayload::IsXwayland => mgr.is_xwayland(), - }; - let cached = Rc::new(CachedCriterion { - crit: criterion.clone(), - cache: cache.clone(), - upstream, - node: matcher.clone(), - }); - cache.set(criterion, Rc::downgrade(&cached)); - self.client_matchers.set(id, cached); - self.respond(Response::CreateClientMatcher { matcher: id }); - Ok(()) - } - - fn handle_destroy_client_matcher(&self, matcher: ClientMatcher) { - self.client_matchers.remove(&matcher); - self.client_matcher_leafs.remove(&matcher); - } - - fn handle_enable_client_matcher_events( - self: &Rc, - matcher: ClientMatcher, - ) -> Result<(), CphError> { - if self.client_matcher_leafs.contains(&matcher) { - return Ok(()); - } - let upstream = self.get_client_matcher(matcher)?; - let slf = self.clone(); - let leaf = self - .state - .cl_matcher_manager - .leaf(&upstream.node, move |id| { - let client = ConfigClient(id.raw()); - slf.send(&ServerMessage::ClientMatcherMatched { matcher, client }); - let slf = slf.clone(); - Box::new(move || { - slf.send(&ServerMessage::ClientMatcherUnmatched { matcher, client }); - }) - }); - self.client_matcher_leafs.set(matcher, leaf); - self.state.cl_matcher_manager.rematch_all(&self.state); - Ok(()) - } - - fn get_window_matcher( - &self, - matcher: WindowMatcher, - ) -> Result>, CphError> { - self.window_matchers - .get(&matcher) - .ok_or(CphError::WindowMatcherDoesNotExist(matcher)) - } - - fn handle_create_window_matcher( - &self, - mut criterion: WindowCriterionPayload, - ) -> Result<(), CphError> { - if let WindowCriterionPayload::Generic(generic) = &mut criterion { - self.sort_generic_matcher(generic, |m| m.0); - } - let id = WindowMatcher(self.window_matcher_ids.fetch_add(1)); - let cache = &self.window_matcher_cache; - if let Some(matcher) = cache.get(&criterion) - && let Some(matcher) = matcher.upgrade() - { - self.window_matchers.set(id, matcher); - self.respond(Response::CreateWindowMatcher { matcher: id }); - return Ok(()); - } - let mgr = &self.state.tl_matcher_manager; - let mut upstream = vec![]; - let matcher = match &criterion { - WindowCriterionPayload::Generic(m) => { - self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_window_matcher(*m))? - } - WindowCriterionPayload::String { - string, - field, - regex, - } => { - let needle = match *regex { - true => { - let regex = Regex::new(string).map_err(CphError::InvalidRegex)?; - CritLiteralOrRegex::Regex(regex) - } - false => CritLiteralOrRegex::Literal(string.to_string()), - }; - match *field { - WindowCriterionStringField::Title => mgr.title(needle), - WindowCriterionStringField::AppId => mgr.app_id(needle), - WindowCriterionStringField::Tag => mgr.tag(needle), - WindowCriterionStringField::XClass => mgr.class(needle), - WindowCriterionStringField::XInstance => mgr.instance(needle), - WindowCriterionStringField::XRole => mgr.role(needle), - WindowCriterionStringField::Workspace => mgr.workspace(needle), - } - } - WindowCriterionPayload::Types(t) => mgr.kind(*t), - WindowCriterionPayload::Client(c) => { - self.state.cl_matcher_manager.rematch_all(&self.state); - mgr.client(&self.state, &self.get_client_matcher(*c)?.node) - } - WindowCriterionPayload::Floating => mgr.floating(), - WindowCriterionPayload::Visible => mgr.visible(), - WindowCriterionPayload::Urgent => mgr.urgent(), - WindowCriterionPayload::SeatFocus(seat) => mgr.seat_focus(&*self.get_seat(*seat)?), - WindowCriterionPayload::Fullscreen => mgr.fullscreen(), - WindowCriterionPayload::JustMapped => mgr.just_mapped(), - WindowCriterionPayload::Workspace(w) => mgr.workspace(CritLiteralOrRegex::Literal( - self.get_workspace(*w)?.to_string(), - )), - WindowCriterionPayload::ContentTypes(t) => mgr.content_type(*t), - }; - let cached = Rc::new(CachedCriterion { - crit: criterion.clone(), - cache: cache.clone(), - upstream, - node: matcher.clone(), - }); - cache.set(criterion, Rc::downgrade(&cached)); - self.window_matchers.set(id, cached); - self.respond(Response::CreateWindowMatcher { matcher: id }); - Ok(()) - } - - fn handle_destroy_window_matcher(&self, matcher: WindowMatcher) { - self.window_matchers.remove(&matcher); - self.window_matcher_leafs.remove(&matcher); - self.window_matcher_no_auto_focus.remove(&matcher); - self.window_matcher_initial_tile_state.remove(&matcher); - } - - fn handle_enable_window_matcher_events( - self: &Rc, - matcher: WindowMatcher, - ) -> Result<(), CphError> { - if self.window_matcher_leafs.contains(&matcher) { - return Ok(()); - } - let upstream = self.get_window_matcher(matcher)?; - let mut node = upstream.node.clone(); - if !upstream.any(&|crit| matches!(crit, WindowCriterionPayload::Types(_))) { - let list = [self.window_matcher_std_kinds.clone(), node]; - node = self.state.tl_matcher_manager.list(&list, true); - } - let slf = self.clone(); - let leaf = self.state.tl_matcher_manager.leaf(&node, move |tl| { - let window = slf.tl_id_to_window(tl); - slf.send(&ServerMessage::WindowMatcherMatched { matcher, window }); - let slf = slf.clone(); - Box::new(move || { - slf.send(&ServerMessage::WindowMatcherUnmatched { matcher, window }); - }) - }); - self.window_matcher_leafs.set(matcher, leaf); - self.state.tl_matcher_manager.rematch_all(&self.state); - Ok(()) - } - - fn handle_set_window_matcher_auto_focus( - &self, - matcher: WindowMatcher, - auto_focus: bool, - ) -> Result<(), CphError> { - if auto_focus { - self.window_matcher_no_auto_focus.remove(&matcher); - } else { - let m = self.get_window_matcher(matcher)?; - self.window_matcher_no_auto_focus.set(matcher, m); - } - Ok(()) - } - - fn handle_set_window_matcher_initial_tile_state( - &self, - matcher: WindowMatcher, - tile_state: ConfigTileState, - ) -> Result<(), CphError> { - let Ok(tile_state) = tile_state.try_into() else { - return Err(CphError::UnknownTileState(tile_state)); - }; - let m = self.get_window_matcher(matcher)?; - self.window_matcher_initial_tile_state - .set(matcher, (m, tile_state)); - Ok(()) - } - fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> { self.get_seat(seat)?.set_pointer_revert_key(key); Ok(()) diff --git a/src/config/handler/matchers.rs b/src/config/handler/matchers.rs new file mode 100644 index 00000000..cc00a346 --- /dev/null +++ b/src/config/handler/matchers.rs @@ -0,0 +1,299 @@ +use super::*; + +impl ConfigProxyHandler { + fn get_client_matcher( + &self, + matcher: ClientMatcher, + ) -> Result>, CphError> { + self.client_matchers + .get(&matcher) + .ok_or(CphError::ClientMatcherDoesNotExist(matcher)) + } + + fn sort_generic_matcher( + &self, + generic: &mut GenericCriterionPayload, + key: impl FnMut(&T) -> K, + ) where + K: Ord, + { + match generic { + GenericCriterionPayload::List { list, .. } | GenericCriterionPayload::Exactly { list, .. } => { + list.sort_by_key(key) + } + GenericCriterionPayload::Matcher(_) | GenericCriterionPayload::Not(_) => {} + } + } + + fn create_generic_matcher( + &self, + mgr: &Mgr, + generic: &GenericCriterionPayload, + upstream: &mut Vec>>, + get_matcher: impl Fn(&Matcher) -> Result>, CphError>, + ) -> Result>, CphError> + where + Crit: Clone + Hash + Eq, + Mgr: CritMgrExt, + { + let mut get_upstream = |m: &Matcher| -> Result<_, CphError> { + let m = get_matcher(m)?; + let node = m.node.clone(); + upstream.push(m); + Ok(node) + }; + let node = match generic { + GenericCriterionPayload::Matcher(m) => get_matcher(m)?.node.clone(), + GenericCriterionPayload::Not(m) => mgr.not(&get_upstream(m)?), + GenericCriterionPayload::List { list, all } => { + let mut m = Vec::with_capacity(list.len()); + for c in list { + m.push(get_upstream(c)?); + } + mgr.list(&m, *all) + } + GenericCriterionPayload::Exactly { list, num } => { + let mut m = Vec::with_capacity(list.len()); + for c in list { + m.push(get_upstream(c)?); + } + mgr.exactly(&m, *num) + } + }; + Ok(node) + } + + pub(super) fn handle_create_client_matcher( + &self, + mut criterion: ClientCriterionPayload, + ) -> Result<(), CphError> { + if let ClientCriterionPayload::Generic(generic) = &mut criterion { + self.sort_generic_matcher(generic, |m| m.0); + } + let id = ClientMatcher(self.client_matcher_ids.fetch_add(1)); + let cache = &self.client_matcher_cache; + if let Some(matcher) = cache.get(&criterion) + && let Some(matcher) = matcher.upgrade() + { + self.client_matchers.set(id, matcher); + self.respond(Response::CreateClientMatcher { matcher: id }); + return Ok(()); + } + let mgr = &self.state.cl_matcher_manager; + let mut upstream = vec![]; + let matcher = match &criterion { + ClientCriterionPayload::Generic(m) => { + self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_client_matcher(*m))? + } + ClientCriterionPayload::String { + string, + field, + regex, + } => { + let needle = match *regex { + true => { + let regex = Regex::new(string).map_err(CphError::InvalidRegex)?; + CritLiteralOrRegex::Regex(regex) + } + false => CritLiteralOrRegex::Literal(string.to_string()), + }; + match *field { + ClientCriterionStringField::SandboxEngine => mgr.sandbox_engine(needle), + ClientCriterionStringField::SandboxAppId => mgr.sandbox_app_id(needle), + ClientCriterionStringField::SandboxInstanceId => { + mgr.sandbox_instance_id(needle) + } + ClientCriterionStringField::Comm => mgr.comm(needle), + ClientCriterionStringField::Exe => mgr.exe(needle), + } + } + ClientCriterionPayload::Sandboxed => mgr.sandboxed(), + ClientCriterionPayload::Uid(p) => mgr.uid(*p), + ClientCriterionPayload::Pid(p) => mgr.pid(*p), + ClientCriterionPayload::IsXwayland => mgr.is_xwayland(), + }; + let cached = Rc::new(CachedCriterion { + crit: criterion.clone(), + cache: cache.clone(), + upstream, + node: matcher.clone(), + }); + cache.set(criterion, Rc::downgrade(&cached)); + self.client_matchers.set(id, cached); + self.respond(Response::CreateClientMatcher { matcher: id }); + Ok(()) + } + + pub(super) fn handle_destroy_client_matcher(&self, matcher: ClientMatcher) { + self.client_matchers.remove(&matcher); + self.client_matcher_leafs.remove(&matcher); + } + + pub(super) fn handle_enable_client_matcher_events( + self: &Rc, + matcher: ClientMatcher, + ) -> Result<(), CphError> { + if self.client_matcher_leafs.contains(&matcher) { + return Ok(()); + } + let upstream = self.get_client_matcher(matcher)?; + let slf = self.clone(); + let leaf = self + .state + .cl_matcher_manager + .leaf(&upstream.node, move |id| { + let client = ConfigClient(id.raw()); + slf.send(&ServerMessage::ClientMatcherMatched { matcher, client }); + let slf = slf.clone(); + Box::new(move || { + slf.send(&ServerMessage::ClientMatcherUnmatched { matcher, client }); + }) + }); + self.client_matcher_leafs.set(matcher, leaf); + self.state.cl_matcher_manager.rematch_all(&self.state); + Ok(()) + } + + fn get_window_matcher( + &self, + matcher: WindowMatcher, + ) -> Result>, CphError> { + self.window_matchers + .get(&matcher) + .ok_or(CphError::WindowMatcherDoesNotExist(matcher)) + } + + pub(super) fn handle_create_window_matcher( + &self, + mut criterion: WindowCriterionPayload, + ) -> Result<(), CphError> { + if let WindowCriterionPayload::Generic(generic) = &mut criterion { + self.sort_generic_matcher(generic, |m| m.0); + } + let id = WindowMatcher(self.window_matcher_ids.fetch_add(1)); + let cache = &self.window_matcher_cache; + if let Some(matcher) = cache.get(&criterion) + && let Some(matcher) = matcher.upgrade() + { + self.window_matchers.set(id, matcher); + self.respond(Response::CreateWindowMatcher { matcher: id }); + return Ok(()); + } + let mgr = &self.state.tl_matcher_manager; + let mut upstream = vec![]; + let matcher = match &criterion { + WindowCriterionPayload::Generic(m) => { + self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_window_matcher(*m))? + } + WindowCriterionPayload::String { + string, + field, + regex, + } => { + let needle = match *regex { + true => { + let regex = Regex::new(string).map_err(CphError::InvalidRegex)?; + CritLiteralOrRegex::Regex(regex) + } + false => CritLiteralOrRegex::Literal(string.to_string()), + }; + match *field { + WindowCriterionStringField::Title => mgr.title(needle), + WindowCriterionStringField::AppId => mgr.app_id(needle), + WindowCriterionStringField::Tag => mgr.tag(needle), + WindowCriterionStringField::XClass => mgr.class(needle), + WindowCriterionStringField::XInstance => mgr.instance(needle), + WindowCriterionStringField::XRole => mgr.role(needle), + WindowCriterionStringField::Workspace => mgr.workspace(needle), + } + } + WindowCriterionPayload::Types(t) => mgr.kind(*t), + WindowCriterionPayload::Client(c) => { + self.state.cl_matcher_manager.rematch_all(&self.state); + mgr.client(&self.state, &self.get_client_matcher(*c)?.node) + } + WindowCriterionPayload::Floating => mgr.floating(), + WindowCriterionPayload::Visible => mgr.visible(), + WindowCriterionPayload::Urgent => mgr.urgent(), + WindowCriterionPayload::SeatFocus(seat) => mgr.seat_focus(&*self.get_seat(*seat)?), + WindowCriterionPayload::Fullscreen => mgr.fullscreen(), + WindowCriterionPayload::JustMapped => mgr.just_mapped(), + WindowCriterionPayload::Workspace(w) => mgr.workspace(CritLiteralOrRegex::Literal( + self.get_workspace(*w)?.to_string(), + )), + WindowCriterionPayload::ContentTypes(t) => mgr.content_type(*t), + }; + let cached = Rc::new(CachedCriterion { + crit: criterion.clone(), + cache: cache.clone(), + upstream, + node: matcher.clone(), + }); + cache.set(criterion, Rc::downgrade(&cached)); + self.window_matchers.set(id, cached); + self.respond(Response::CreateWindowMatcher { matcher: id }); + Ok(()) + } + + pub(super) fn handle_destroy_window_matcher(&self, matcher: WindowMatcher) { + self.window_matchers.remove(&matcher); + self.window_matcher_leafs.remove(&matcher); + self.window_matcher_no_auto_focus.remove(&matcher); + self.window_matcher_initial_tile_state.remove(&matcher); + } + + pub(super) fn handle_enable_window_matcher_events( + self: &Rc, + matcher: WindowMatcher, + ) -> Result<(), CphError> { + if self.window_matcher_leafs.contains(&matcher) { + return Ok(()); + } + let upstream = self.get_window_matcher(matcher)?; + let mut node = upstream.node.clone(); + if !upstream.any(&|crit| matches!(crit, WindowCriterionPayload::Types(_))) { + let list = [self.window_matcher_std_kinds.clone(), node]; + node = self.state.tl_matcher_manager.list(&list, true); + } + let slf = self.clone(); + let leaf = self.state.tl_matcher_manager.leaf(&node, move |tl| { + let window = slf.tl_id_to_window(tl); + slf.send(&ServerMessage::WindowMatcherMatched { matcher, window }); + let slf = slf.clone(); + Box::new(move || { + slf.send(&ServerMessage::WindowMatcherUnmatched { matcher, window }); + }) + }); + self.window_matcher_leafs.set(matcher, leaf); + self.state.tl_matcher_manager.rematch_all(&self.state); + Ok(()) + } + + pub(super) fn handle_set_window_matcher_auto_focus( + &self, + matcher: WindowMatcher, + auto_focus: bool, + ) -> Result<(), CphError> { + if auto_focus { + self.window_matcher_no_auto_focus.remove(&matcher); + } else { + let m = self.get_window_matcher(matcher)?; + self.window_matcher_no_auto_focus.set(matcher, m); + } + Ok(()) + } + + pub(super) fn handle_set_window_matcher_initial_tile_state( + &self, + matcher: WindowMatcher, + tile_state: ConfigTileState, + ) -> Result<(), CphError> { + let Ok(tile_state) = tile_state.try_into() else { + return Err(CphError::UnknownTileState(tile_state)); + }; + let m = self.get_window_matcher(matcher)?; + self.window_matcher_initial_tile_state + .set(matcher, (m, tile_state)); + Ok(()) + } +}