diff --git a/jay-config/src/_private.rs b/jay-config/src/_private.rs index 6d0c1293..bd7bc94a 100644 --- a/jay-config/src/_private.rs +++ b/jay-config/src/_private.rs @@ -112,6 +112,7 @@ pub enum WindowCriterionIpc { Client(ClientMatcher), Floating, Visible, + Urgent, } #[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)] diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 5ab093bd..0881d6bb 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -1659,6 +1659,7 @@ impl ConfigClient { WindowCriterion::AppIdRegex(t) => string!(t, AppId, true), WindowCriterion::Floating => WindowCriterionIpc::Floating, WindowCriterion::Visible => WindowCriterionIpc::Visible, + WindowCriterion::Urgent => WindowCriterionIpc::Urgent, }; let res = self.send_with_response(&ClientMessage::CreateWindowMatcher { criterion }); get_response!( diff --git a/jay-config/src/window.rs b/jay-config/src/window.rs index 9415fed9..0d3e1127 100644 --- a/jay-config/src/window.rs +++ b/jay-config/src/window.rs @@ -248,6 +248,8 @@ pub enum WindowCriterion<'a> { Floating, /// Matches if the window is visible. Visible, + /// Matches if the window has the urgency flag set. + Urgent, } impl WindowCriterion<'_> { diff --git a/src/config/handler.rs b/src/config/handler.rs index a98b36fa..bdd5fb66 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1999,6 +1999,7 @@ impl ConfigProxyHandler { } WindowCriterionIpc::Floating => mgr.floating(), WindowCriterionIpc::Visible => mgr.visible(), + WindowCriterionIpc::Urgent => mgr.urgent(), }; let cached = Rc::new(CachedCriterion { crit: criterion.clone(), diff --git a/src/criteria/tlm.rs b/src/criteria/tlm.rs index a24dfff9..af9797db 100644 --- a/src/criteria/tlm.rs +++ b/src/criteria/tlm.rs @@ -16,6 +16,7 @@ use { tlmm_floating::TlmMatchFloating, tlmm_kind::TlmMatchKind, tlmm_string::{TlmMatchAppId, TlmMatchTitle}, + tlmm_urgent::TlmMatchUrgent, tlmm_visible::TlmMatchVisible, }, }, @@ -42,6 +43,7 @@ bitflags! { TL_CHANGED_APP_ID = 1 << 3, TL_CHANGED_FLOATING = 1 << 4, TL_CHANGED_VISIBLE = 1 << 5, + TL_CHANGED_URGENT = 1 << 6, } type TlmFixedRootMatcher = FixedRootMatcher; @@ -53,6 +55,7 @@ pub struct TlMatcherManager { constant: TlmFixedRootMatcher>, floating: TlmFixedRootMatcher, visible: TlmFixedRootMatcher, + urgent: TlmFixedRootMatcher, matchers: Rc, } @@ -105,6 +108,7 @@ impl TlMatcherManager { constant: CritMatchConstant::create(&matchers, ids), floating: bool!(TlmMatchFloating), visible: bool!(TlmMatchVisible), + urgent: bool!(TlmMatchUrgent), changes: Default::default(), leaf_events: Default::default(), ids: ids.clone(), @@ -173,6 +177,7 @@ impl TlMatcherManager { conditional!(TL_CHANGED_APP_ID, app_id); fixed_conditional!(TL_CHANGED_FLOATING, floating); fixed_conditional!(TL_CHANGED_VISIBLE, visible); + fixed_conditional!(TL_CHANGED_URGENT, urgent); false } @@ -241,6 +246,7 @@ impl TlMatcherManager { conditional!(TL_CHANGED_APP_ID, app_id); fixed_conditional!(TL_CHANGED_FLOATING, floating); fixed_conditional!(TL_CHANGED_VISIBLE, visible); + fixed_conditional!(TL_CHANGED_URGENT, urgent); } pub fn title(&self, string: CritLiteralOrRegex) -> Rc { @@ -266,6 +272,10 @@ impl TlMatcherManager { pub fn visible(&self) -> Rc { self.visible[true].clone() } + + pub fn urgent(&self) -> Rc { + self.urgent[true].clone() + } } impl CritTarget for ToplevelData { diff --git a/src/criteria/tlm/tlm_matchers.rs b/src/criteria/tlm/tlm_matchers.rs index bb84be5b..fcff929e 100644 --- a/src/criteria/tlm/tlm_matchers.rs +++ b/src/criteria/tlm/tlm_matchers.rs @@ -21,4 +21,5 @@ pub mod tlmm_client; pub mod tlmm_floating; pub mod tlmm_kind; pub mod tlmm_string; +pub mod tlmm_urgent; pub mod tlmm_visible; diff --git a/src/criteria/tlm/tlm_matchers/tlmm_urgent.rs b/src/criteria/tlm/tlm_matchers/tlmm_urgent.rs new file mode 100644 index 00000000..157135ad --- /dev/null +++ b/src/criteria/tlm/tlm_matchers/tlmm_urgent.rs @@ -0,0 +1,11 @@ +use crate::{criteria::crit_graph::CritFixedRootCriterion, tree::ToplevelData}; + +pub struct TlmMatchUrgent(pub bool); + +fixed_root_criterion!(TlmMatchUrgent, urgent); + +impl CritFixedRootCriterion for TlmMatchUrgent { + fn matches(&self, data: &ToplevelData) -> bool { + data.wants_attention.get() + } +} diff --git a/src/tree/container.rs b/src/tree/container.rs index fd56767f..06e075ca 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -1152,7 +1152,7 @@ impl ContainerNode { fn mod_attention_requests(&self, set: bool) { let propagate = self.attention_requests.adj(set); if set || propagate { - self.toplevel_data.wants_attention.set(set); + self.toplevel_data.set_wants_attention(set); } if propagate { if let Some(parent) = self.toplevel_data.parent.get() { diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index fc5886dc..36f77f14 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -5,7 +5,7 @@ use { CritDestroyListener, CritMatcherId, tlm::{ TL_CHANGED_APP_ID, TL_CHANGED_DESTROYED, TL_CHANGED_FLOATING, TL_CHANGED_NEW, - TL_CHANGED_TITLE, TL_CHANGED_VISIBLE, TlMatcherChange, + TL_CHANGED_TITLE, TL_CHANGED_URGENT, TL_CHANGED_VISIBLE, TlMatcherChange, }, }, ifs::{ @@ -647,7 +647,7 @@ impl ToplevelData { if !self.requested_attention.replace(false) { return; } - self.wants_attention.set(false); + self.set_wants_attention(false); if let Some(parent) = self.parent.get() { parent.cnode_child_attention_request_changed(node, false); } @@ -660,12 +660,18 @@ impl ToplevelData { if self.requested_attention.replace(true) { return; } - self.wants_attention.set(true); + self.set_wants_attention(true); if let Some(parent) = self.parent.get() { parent.cnode_child_attention_request_changed(node, true); } } + pub fn set_wants_attention(&self, value: bool) { + if self.wants_attention.replace(value) != value { + self.property_changed(TL_CHANGED_URGENT); + } + } + pub fn output(&self) -> Rc { match self.output_opt() { None => self.state.dummy_output.get().unwrap(), diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index f84a4034..5745898b 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -261,6 +261,7 @@ pub struct WindowMatch { pub app_id_regex: Option, pub floating: Option, pub visible: Option, + pub urgent: Option, } #[derive(Debug, Clone)] diff --git a/toml-config/src/config/parsers/window_match.rs b/toml-config/src/config/parsers/window_match.rs index 1a2b1f3e..69ca4eea 100644 --- a/toml-config/src/config/parsers/window_match.rs +++ b/toml-config/src/config/parsers/window_match.rs @@ -56,7 +56,7 @@ impl Parser for WindowMatchParser<'_> { title, title_regex, ), - (app_id, app_id_regex, floating, visible), + (app_id, app_id_regex, floating, visible, urgent), ) = ext.extract(( ( opt(str("name")), @@ -74,6 +74,7 @@ impl Parser for WindowMatchParser<'_> { opt(str("app-id-regex")), opt(bol("floating")), opt(bol("visible")), + opt(bol("urgent")), ), ))?; let mut not = None; @@ -121,6 +122,7 @@ impl Parser for WindowMatchParser<'_> { app_id_regex: app_id_regex.despan_into(), floating: floating.despan(), visible: visible.despan(), + urgent: urgent.despan(), types, client, }) diff --git a/toml-config/src/rules.rs b/toml-config/src/rules.rs index 2a7de27a..65062aaa 100644 --- a/toml-config/src/rules.rs +++ b/toml-config/src/rules.rs @@ -260,6 +260,7 @@ impl Rule for WindowRule { value!(AppIdRegex, app_id_regex); bool!(Floating, floating); bool!(Visible, visible); + bool!(Urgent, urgent); Some(()) } diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 6b21ec88..1a37fd8a 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -1803,6 +1803,10 @@ "visible": { "type": "boolean", "description": "Matches if the window is/isn't visible." + }, + "urgent": { + "type": "boolean", + "description": "Matches if the window has/hasn't the urgency flag set." } }, "required": [] diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index cb55fd88..89a1103b 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -4046,6 +4046,12 @@ The table has the following fields: The value of this field should be a boolean. +- `urgent` (optional): + + Matches if the window has/hasn't the urgency flag set. + + The value of this field should be a boolean. + ### `WindowMatchExactly` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 4bcdbd34..bf817b84 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -3491,6 +3491,10 @@ WindowMatch: kind: boolean required: false description: Matches if the window is/isn't visible. + urgent: + kind: boolean + required: false + description: Matches if the window has/hasn't the urgency flag set. WindowMatchExactly: