config: add toplevel-tag window criteria
This commit is contained in:
parent
5f1268cada
commit
6d3d4dcabb
15 changed files with 106 additions and 8 deletions
|
|
@ -123,4 +123,5 @@ pub enum WindowCriterionIpc {
|
||||||
pub enum WindowCriterionStringField {
|
pub enum WindowCriterionStringField {
|
||||||
Title,
|
Title,
|
||||||
AppId,
|
AppId,
|
||||||
|
Tag,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1663,6 +1663,8 @@ impl ConfigClient {
|
||||||
WindowCriterion::Focus(seat) => WindowCriterionIpc::SeatFocus(seat),
|
WindowCriterion::Focus(seat) => WindowCriterionIpc::SeatFocus(seat),
|
||||||
WindowCriterion::Fullscreen => WindowCriterionIpc::Fullscreen,
|
WindowCriterion::Fullscreen => WindowCriterionIpc::Fullscreen,
|
||||||
WindowCriterion::JustMapped => WindowCriterionIpc::JustMapped,
|
WindowCriterion::JustMapped => WindowCriterionIpc::JustMapped,
|
||||||
|
WindowCriterion::Tag(t) => string!(t, Tag, false),
|
||||||
|
WindowCriterion::TagRegex(t) => string!(t, Tag, true),
|
||||||
};
|
};
|
||||||
let res = self.send_with_response(&ClientMessage::CreateWindowMatcher { criterion });
|
let res = self.send_with_response(&ClientMessage::CreateWindowMatcher { criterion });
|
||||||
get_response!(
|
get_response!(
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,10 @@ pub enum WindowCriterion<'a> {
|
||||||
/// This is true for one iteration of the compositor's main loop immediately after the
|
/// This is true for one iteration of the compositor's main loop immediately after the
|
||||||
/// window has been mapped.
|
/// window has been mapped.
|
||||||
JustMapped,
|
JustMapped,
|
||||||
|
/// Matches the toplevel-tag of the window verbatim.
|
||||||
|
Tag(&'a str),
|
||||||
|
/// Matches the toplevel-tag of the window with a regular expression.
|
||||||
|
TagRegex(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowCriterion<'_> {
|
impl WindowCriterion<'_> {
|
||||||
|
|
|
||||||
|
|
@ -1990,6 +1990,7 @@ impl ConfigProxyHandler {
|
||||||
match *field {
|
match *field {
|
||||||
WindowCriterionStringField::Title => mgr.title(needle),
|
WindowCriterionStringField::Title => mgr.title(needle),
|
||||||
WindowCriterionStringField::AppId => mgr.app_id(needle),
|
WindowCriterionStringField::AppId => mgr.app_id(needle),
|
||||||
|
WindowCriterionStringField::Tag => mgr.tag(needle),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowCriterionIpc::Types(t) => mgr.kind(*t),
|
WindowCriterionIpc::Types(t) => mgr.kind(*t),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use {
|
||||||
tlmm_just_mapped::TlmMatchJustMapped,
|
tlmm_just_mapped::TlmMatchJustMapped,
|
||||||
tlmm_kind::TlmMatchKind,
|
tlmm_kind::TlmMatchKind,
|
||||||
tlmm_seat_focus::TlmMatchSeatFocus,
|
tlmm_seat_focus::TlmMatchSeatFocus,
|
||||||
tlmm_string::{TlmMatchAppId, TlmMatchTitle},
|
tlmm_string::{TlmMatchAppId, TlmMatchTag, TlmMatchTitle},
|
||||||
tlmm_urgent::TlmMatchUrgent,
|
tlmm_urgent::TlmMatchUrgent,
|
||||||
tlmm_visible::TlmMatchVisible,
|
tlmm_visible::TlmMatchVisible,
|
||||||
},
|
},
|
||||||
|
|
@ -51,6 +51,7 @@ bitflags! {
|
||||||
TL_CHANGED_SEAT_FOCI = 1 << 7,
|
TL_CHANGED_SEAT_FOCI = 1 << 7,
|
||||||
TL_CHANGED_FULLSCREEN = 1 << 8,
|
TL_CHANGED_FULLSCREEN = 1 << 8,
|
||||||
TL_CHANGED_JUST_MAPPED = 1 << 9,
|
TL_CHANGED_JUST_MAPPED = 1 << 9,
|
||||||
|
TL_CHANGED_TAG = 1 << 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
type TlmFixedRootMatcher<T> = FixedRootMatcher<ToplevelData, T>;
|
type TlmFixedRootMatcher<T> = FixedRootMatcher<ToplevelData, T>;
|
||||||
|
|
@ -76,6 +77,7 @@ pub struct RootMatchers {
|
||||||
kinds: TlmRootMatcherMap<TlmMatchKind>,
|
kinds: TlmRootMatcherMap<TlmMatchKind>,
|
||||||
clients: CopyHashMap<CritMatcherId, Weak<TlmMatchClient>>,
|
clients: CopyHashMap<CritMatcherId, Weak<TlmMatchClient>>,
|
||||||
title: TlmRootMatcherMap<TlmMatchTitle>,
|
title: TlmRootMatcherMap<TlmMatchTitle>,
|
||||||
|
tag: TlmRootMatcherMap<TlmMatchTag>,
|
||||||
app_id: TlmRootMatcherMap<TlmMatchAppId>,
|
app_id: TlmRootMatcherMap<TlmMatchAppId>,
|
||||||
seat_foci: TlmRootMatcherMap<TlmMatchSeatFocus>,
|
seat_foci: TlmRootMatcherMap<TlmMatchSeatFocus>,
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +207,7 @@ impl TlMatcherManager {
|
||||||
conditional!(TL_CHANGED_TITLE, title);
|
conditional!(TL_CHANGED_TITLE, title);
|
||||||
conditional!(TL_CHANGED_APP_ID, app_id);
|
conditional!(TL_CHANGED_APP_ID, app_id);
|
||||||
conditional!(TL_CHANGED_SEAT_FOCI, seat_foci);
|
conditional!(TL_CHANGED_SEAT_FOCI, seat_foci);
|
||||||
|
conditional!(TL_CHANGED_TAG, tag);
|
||||||
fixed_conditional!(TL_CHANGED_FLOATING, floating);
|
fixed_conditional!(TL_CHANGED_FLOATING, floating);
|
||||||
fixed_conditional!(TL_CHANGED_VISIBLE, visible);
|
fixed_conditional!(TL_CHANGED_VISIBLE, visible);
|
||||||
fixed_conditional!(TL_CHANGED_URGENT, urgent);
|
fixed_conditional!(TL_CHANGED_URGENT, urgent);
|
||||||
|
|
@ -277,6 +280,7 @@ impl TlMatcherManager {
|
||||||
conditional!(TL_CHANGED_TITLE, title);
|
conditional!(TL_CHANGED_TITLE, title);
|
||||||
conditional!(TL_CHANGED_APP_ID, app_id);
|
conditional!(TL_CHANGED_APP_ID, app_id);
|
||||||
conditional!(TL_CHANGED_SEAT_FOCI, seat_foci);
|
conditional!(TL_CHANGED_SEAT_FOCI, seat_foci);
|
||||||
|
conditional!(TL_CHANGED_TAG, tag);
|
||||||
fixed_conditional!(TL_CHANGED_FLOATING, floating);
|
fixed_conditional!(TL_CHANGED_FLOATING, floating);
|
||||||
fixed_conditional!(TL_CHANGED_VISIBLE, visible);
|
fixed_conditional!(TL_CHANGED_VISIBLE, visible);
|
||||||
fixed_conditional!(TL_CHANGED_URGENT, urgent);
|
fixed_conditional!(TL_CHANGED_URGENT, urgent);
|
||||||
|
|
@ -299,6 +303,10 @@ impl TlMatcherManager {
|
||||||
self.root(TlmMatchAppId::new(string))
|
self.root(TlmMatchAppId::new(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tag(&self, string: CritLiteralOrRegex) -> Rc<TlmUpstreamNode> {
|
||||||
|
self.root(TlmMatchTag::new(string))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn floating(&self) -> Rc<TlmUpstreamNode> {
|
pub fn floating(&self) -> Rc<TlmUpstreamNode> {
|
||||||
self.floating[true].clone()
|
self.floating[true].clone()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,18 @@ use crate::{
|
||||||
crit_matchers::critm_string::{CritMatchString, StringAccess},
|
crit_matchers::critm_string::{CritMatchString, StringAccess},
|
||||||
tlm::{RootMatchers, TlmRootMatcherMap},
|
tlm::{RootMatchers, TlmRootMatcherMap},
|
||||||
},
|
},
|
||||||
tree::ToplevelData,
|
tree::{ToplevelData, ToplevelType},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type TlmMatchString<T> = CritMatchString<ToplevelData, T>;
|
pub type TlmMatchString<T> = CritMatchString<ToplevelData, T>;
|
||||||
|
|
||||||
pub type TlmMatchTitle = TlmMatchString<TitleAccess>;
|
pub type TlmMatchTitle = TlmMatchString<TitleAccess>;
|
||||||
pub type TlmMatchAppId = TlmMatchString<AppIdAccess>;
|
pub type TlmMatchAppId = TlmMatchString<AppIdAccess>;
|
||||||
|
pub type TlmMatchTag = TlmMatchString<TagAccess>;
|
||||||
|
|
||||||
pub struct TitleAccess;
|
pub struct TitleAccess;
|
||||||
pub struct AppIdAccess;
|
pub struct AppIdAccess;
|
||||||
|
pub struct TagAccess;
|
||||||
|
|
||||||
impl StringAccess<ToplevelData> for TitleAccess {
|
impl StringAccess<ToplevelData> for TitleAccess {
|
||||||
fn with_string(data: &ToplevelData, f: impl FnOnce(&str) -> bool) -> bool {
|
fn with_string(data: &ToplevelData, f: impl FnOnce(&str) -> bool) -> bool {
|
||||||
|
|
@ -33,3 +35,16 @@ impl StringAccess<ToplevelData> for AppIdAccess {
|
||||||
&roots.app_id
|
&roots.app_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StringAccess<ToplevelData> for TagAccess {
|
||||||
|
fn with_string(data: &ToplevelData, f: impl FnOnce(&str) -> bool) -> bool {
|
||||||
|
if let ToplevelType::XdgToplevel(data) = &data.kind {
|
||||||
|
return f(&data.tag.borrow());
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nodes(roots: &RootMatchers) -> &TlmRootMatcherMap<TlmMatchString<Self>> {
|
||||||
|
&roots.tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,11 @@ pub enum Decoration {
|
||||||
Server,
|
Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct XdgToplevelToplevelData {
|
||||||
|
pub tag: RefCell<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct XdgToplevel {
|
pub struct XdgToplevel {
|
||||||
pub id: XdgToplevelId,
|
pub id: XdgToplevelId,
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
|
|
@ -112,6 +117,7 @@ pub struct XdgToplevel {
|
||||||
is_mapped: Cell<bool>,
|
is_mapped: Cell<bool>,
|
||||||
dialog: CloneCell<Option<Rc<XdgDialogV1>>>,
|
dialog: CloneCell<Option<Rc<XdgDialogV1>>>,
|
||||||
extents_set: Cell<bool>,
|
extents_set: Cell<bool>,
|
||||||
|
pub data: Rc<XdgToplevelToplevelData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for XdgToplevel {
|
impl Debug for XdgToplevel {
|
||||||
|
|
@ -135,6 +141,9 @@ impl XdgToplevel {
|
||||||
}
|
}
|
||||||
let state = &surface.surface.client.state;
|
let state = &surface.surface.client.state;
|
||||||
let node_id = state.node_ids.next();
|
let node_id = state.node_ids.next();
|
||||||
|
let data = Rc::new(XdgToplevelToplevelData {
|
||||||
|
tag: Default::default(),
|
||||||
|
});
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
|
|
@ -154,7 +163,7 @@ impl XdgToplevel {
|
||||||
state,
|
state,
|
||||||
String::new(),
|
String::new(),
|
||||||
Some(surface.surface.client.clone()),
|
Some(surface.surface.client.clone()),
|
||||||
ToplevelType::XdgToplevel,
|
ToplevelType::XdgToplevel(data.clone()),
|
||||||
node_id,
|
node_id,
|
||||||
slf,
|
slf,
|
||||||
),
|
),
|
||||||
|
|
@ -162,6 +171,7 @@ impl XdgToplevel {
|
||||||
is_mapped: Cell::new(false),
|
is_mapped: Cell::new(false),
|
||||||
dialog: Default::default(),
|
dialog: Default::default(),
|
||||||
extents_set: Cell::new(false),
|
extents_set: Cell::new(false),
|
||||||
|
data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
criteria::tlm::TL_CHANGED_TAG,
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
tree::ToplevelNodeBase,
|
||||||
wire::{XdgToplevelTagManagerV1Id, xdg_toplevel_tag_manager_v1::*},
|
wire::{XdgToplevelTagManagerV1Id, xdg_toplevel_tag_manager_v1::*},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
|
|
@ -72,9 +74,17 @@ impl XdgToplevelTagManagerV1RequestHandler for XdgToplevelTagManagerV1 {
|
||||||
|
|
||||||
fn set_toplevel_tag(
|
fn set_toplevel_tag(
|
||||||
&self,
|
&self,
|
||||||
_req: SetToplevelTag<'_>,
|
req: SetToplevelTag<'_>,
|
||||||
_slf: &Rc<Self>,
|
_slf: &Rc<Self>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
|
let tl = self.client.lookup(req.toplevel)?;
|
||||||
|
let tag = &mut *tl.data.tag.borrow_mut();
|
||||||
|
if tag == req.tag {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tag.clear();
|
||||||
|
tag.push_str(req.tag);
|
||||||
|
tl.tl_data().property_changed(TL_CHANGED_TAG);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use {
|
||||||
jay_screencast::JayScreencast,
|
jay_screencast::JayScreencast,
|
||||||
jay_toplevel::JayToplevel,
|
jay_toplevel::JayToplevel,
|
||||||
wl_seat::{NodeSeatState, SeatId, collect_kb_foci, collect_kb_foci2},
|
wl_seat::{NodeSeatState, SeatId, collect_kb_foci, collect_kb_foci2},
|
||||||
wl_surface::WlSurface,
|
wl_surface::{WlSurface, xdg_surface::xdg_toplevel::XdgToplevelToplevelData},
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -277,7 +277,7 @@ impl ToplevelOpt {
|
||||||
pub enum ToplevelType {
|
pub enum ToplevelType {
|
||||||
Container,
|
Container,
|
||||||
Placeholder,
|
Placeholder,
|
||||||
XdgToplevel,
|
XdgToplevel(Rc<XdgToplevelToplevelData>),
|
||||||
XWindow,
|
XWindow,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,7 +286,7 @@ impl ToplevelType {
|
||||||
match self {
|
match self {
|
||||||
ToplevelType::Container => window::CONTAINER,
|
ToplevelType::Container => window::CONTAINER,
|
||||||
ToplevelType::Placeholder => window::PLACEHOLDER,
|
ToplevelType::Placeholder => window::PLACEHOLDER,
|
||||||
ToplevelType::XdgToplevel => window::XDG_TOPLEVEL,
|
ToplevelType::XdgToplevel { .. } => window::XDG_TOPLEVEL,
|
||||||
ToplevelType::XWindow => window::X_WINDOW,
|
ToplevelType::XWindow => window::X_WINDOW,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,8 @@ pub struct WindowMatch {
|
||||||
pub focused: Option<bool>,
|
pub focused: Option<bool>,
|
||||||
pub fullscreen: Option<bool>,
|
pub fullscreen: Option<bool>,
|
||||||
pub just_mapped: Option<bool>,
|
pub just_mapped: Option<bool>,
|
||||||
|
pub tag: Option<String>,
|
||||||
|
pub tag_regex: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,18 @@ impl Parser for WindowMatchParser<'_> {
|
||||||
title,
|
title,
|
||||||
title_regex,
|
title_regex,
|
||||||
),
|
),
|
||||||
(app_id, app_id_regex, floating, visible, urgent, focused, fullscreen, just_mapped),
|
(
|
||||||
|
app_id,
|
||||||
|
app_id_regex,
|
||||||
|
floating,
|
||||||
|
visible,
|
||||||
|
urgent,
|
||||||
|
focused,
|
||||||
|
fullscreen,
|
||||||
|
just_mapped,
|
||||||
|
tag,
|
||||||
|
tag_regex,
|
||||||
|
),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(str("name")),
|
opt(str("name")),
|
||||||
|
|
@ -78,6 +89,8 @@ impl Parser for WindowMatchParser<'_> {
|
||||||
opt(bol("focused")),
|
opt(bol("focused")),
|
||||||
opt(bol("fullscreen")),
|
opt(bol("fullscreen")),
|
||||||
opt(bol("just-mapped")),
|
opt(bol("just-mapped")),
|
||||||
|
opt(str("tag")),
|
||||||
|
opt(str("tag-regex")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut not = None;
|
let mut not = None;
|
||||||
|
|
@ -129,6 +142,8 @@ impl Parser for WindowMatchParser<'_> {
|
||||||
focused: focused.despan(),
|
focused: focused.despan(),
|
||||||
fullscreen: fullscreen.despan(),
|
fullscreen: fullscreen.despan(),
|
||||||
just_mapped: just_mapped.despan(),
|
just_mapped: just_mapped.despan(),
|
||||||
|
tag: tag.despan_into(),
|
||||||
|
tag_regex: tag_regex.despan_into(),
|
||||||
types,
|
types,
|
||||||
client,
|
client,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,8 @@ impl Rule for WindowRule {
|
||||||
value!(TitleRegex, title_regex);
|
value!(TitleRegex, title_regex);
|
||||||
value!(AppId, app_id);
|
value!(AppId, app_id);
|
||||||
value!(AppIdRegex, app_id_regex);
|
value!(AppIdRegex, app_id_regex);
|
||||||
|
value!(Tag, tag);
|
||||||
|
value!(TagRegex, tag_regex);
|
||||||
bool!(Floating, floating);
|
bool!(Floating, floating);
|
||||||
bool!(Visible, visible);
|
bool!(Visible, visible);
|
||||||
bool!(Urgent, urgent);
|
bool!(Urgent, urgent);
|
||||||
|
|
|
||||||
|
|
@ -1819,6 +1819,14 @@
|
||||||
"just-mapped": {
|
"just-mapped": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Matches if the window has/hasn't just been mapped.\n\nThis is true for one iteration of the compositor's main loop immediately after the\nwindow has been mapped.\n"
|
"description": "Matches if the window has/hasn't just been mapped.\n\nThis is true for one iteration of the compositor's main loop immediately after the\nwindow has been mapped.\n"
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Matches the toplevel-tag of the window verbatim."
|
||||||
|
},
|
||||||
|
"tag-regex": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Matches the toplevel-tag of the window with a regular expression."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
|
||||||
|
|
@ -4073,6 +4073,18 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a boolean.
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
- `tag` (optional):
|
||||||
|
|
||||||
|
Matches the toplevel-tag of the window verbatim.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
- `tag-regex` (optional):
|
||||||
|
|
||||||
|
Matches the toplevel-tag of the window with a regular expression.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-WindowMatchExactly"></a>
|
<a name="types-WindowMatchExactly"></a>
|
||||||
### `WindowMatchExactly`
|
### `WindowMatchExactly`
|
||||||
|
|
|
||||||
|
|
@ -3511,6 +3511,14 @@ WindowMatch:
|
||||||
|
|
||||||
This is true for one iteration of the compositor's main loop immediately after the
|
This is true for one iteration of the compositor's main loop immediately after the
|
||||||
window has been mapped.
|
window has been mapped.
|
||||||
|
tag:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: Matches the toplevel-tag of the window verbatim.
|
||||||
|
tag-regex:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: Matches the toplevel-tag of the window with a regular expression.
|
||||||
|
|
||||||
|
|
||||||
WindowMatchExactly:
|
WindowMatchExactly:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue