config: split matcher handling
This commit is contained in:
parent
899d39a320
commit
f4fdf750ec
2 changed files with 300 additions and 296 deletions
|
|
@ -88,6 +88,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
mod dispatch;
|
mod dispatch;
|
||||||
|
mod matchers;
|
||||||
|
|
||||||
pub(super) struct ConfigProxyHandler {
|
pub(super) struct ConfigProxyHandler {
|
||||||
pub client_data: Cell<*const u8>,
|
pub client_data: Cell<*const u8>,
|
||||||
|
|
@ -2071,302 +2072,6 @@ impl ConfigProxyHandler {
|
||||||
.ok_or(CphError::WindowDoesNotExist(window))
|
.ok_or(CphError::WindowDoesNotExist(window))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_client_matcher(
|
|
||||||
&self,
|
|
||||||
matcher: ClientMatcher,
|
|
||||||
) -> Result<Rc<CachedCriterion<ClientCriterionPayload, Client>>, CphError> {
|
|
||||||
self.client_matchers
|
|
||||||
.get(&matcher)
|
|
||||||
.ok_or(CphError::ClientMatcherDoesNotExist(matcher))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sort_generic_matcher<T, K>(
|
|
||||||
&self,
|
|
||||||
generic: &mut GenericCriterionPayload<T>,
|
|
||||||
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<Crit, Matcher, Mgr>(
|
|
||||||
&self,
|
|
||||||
mgr: &Mgr,
|
|
||||||
generic: &GenericCriterionPayload<Matcher>,
|
|
||||||
upstream: &mut Vec<Rc<CachedCriterion<Crit, Mgr::Target>>>,
|
|
||||||
get_matcher: impl Fn(&Matcher) -> Result<Rc<CachedCriterion<Crit, Mgr::Target>>, CphError>,
|
|
||||||
) -> Result<Rc<dyn CritUpstreamNode<Mgr::Target>>, 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<Self>,
|
|
||||||
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<Rc<CachedCriterion<WindowCriterionPayload, ToplevelData>>, 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<Self>,
|
|
||||||
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> {
|
fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> {
|
||||||
self.get_seat(seat)?.set_pointer_revert_key(key);
|
self.get_seat(seat)?.set_pointer_revert_key(key);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
299
src/config/handler/matchers.rs
Normal file
299
src/config/handler/matchers.rs
Normal file
|
|
@ -0,0 +1,299 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl ConfigProxyHandler {
|
||||||
|
fn get_client_matcher(
|
||||||
|
&self,
|
||||||
|
matcher: ClientMatcher,
|
||||||
|
) -> Result<Rc<CachedCriterion<ClientCriterionPayload, Client>>, CphError> {
|
||||||
|
self.client_matchers
|
||||||
|
.get(&matcher)
|
||||||
|
.ok_or(CphError::ClientMatcherDoesNotExist(matcher))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_generic_matcher<T, K>(
|
||||||
|
&self,
|
||||||
|
generic: &mut GenericCriterionPayload<T>,
|
||||||
|
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<Crit, Matcher, Mgr>(
|
||||||
|
&self,
|
||||||
|
mgr: &Mgr,
|
||||||
|
generic: &GenericCriterionPayload<Matcher>,
|
||||||
|
upstream: &mut Vec<Rc<CachedCriterion<Crit, Mgr::Target>>>,
|
||||||
|
get_matcher: impl Fn(&Matcher) -> Result<Rc<CachedCriterion<Crit, Mgr::Target>>, CphError>,
|
||||||
|
) -> Result<Rc<dyn CritUpstreamNode<Mgr::Target>>, 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<Self>,
|
||||||
|
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<Rc<CachedCriterion<WindowCriterionPayload, ToplevelData>>, 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<Self>,
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue