i3status_rs/blocks/
focused_window.rs1mod sway_ipc;
39mod wlr_toplevel_management;
40
41use sway_ipc::SwayIpc;
42use wlr_toplevel_management::WlrToplevelManagement;
43
44use super::prelude::*;
45
46#[derive(Deserialize, Debug, SmartDefault)]
47#[serde(deny_unknown_fields, default)]
48pub struct Config {
49 pub format: FormatConfig,
50 pub driver: Driver,
51}
52
53#[derive(Deserialize, Debug, SmartDefault)]
54#[serde(rename_all = "snake_case")]
55pub enum Driver {
56 #[default]
57 Auto,
58 SwayIpc,
59 WlrToplevelManagement,
60}
61
62pub(crate) fn prepare(config: &Config) -> Result<Arc<BlockPlan>> {
63 BlockPlan::new(vec![OutputPlan::new(
67 "main",
68 config.format.with_default(" $title.str(max_w:21) |")?,
69 )])
70}
71
72pub(crate) async fn run(config: &Config, api: &CommonApi, plan: &Arc<BlockPlan>) -> Result<()> {
73 let output_main = plan.output("main")?;
74
75 let mut backend: Box<dyn Backend> = match config.driver {
76 Driver::Auto => match SwayIpc::new().await {
77 Ok(swayipc) => Box::new(swayipc),
78 Err(_) => Box::new(WlrToplevelManagement::new().await?),
79 },
80 Driver::SwayIpc => Box::new(SwayIpc::new().await?),
81 Driver::WlrToplevelManagement => Box::new(WlrToplevelManagement::new().await?),
82 };
83
84 loop {
85 let Info { title, marks } = backend.get_info().await?;
86
87 let mut widget = output_main.new_widget();
88
89 if !title.is_empty() {
90 let join_marks = |mut s: String, m: &String| {
91 let _ = write!(s, "[{m}]"); s
93 };
94
95 let marks_str = marks.iter().fold(String::new(), join_marks);
96 let visible_marks_str = marks
97 .iter()
98 .filter(|m| !m.starts_with('_'))
99 .fold(String::new(), join_marks);
100
101 widget.set_values(map! {
102 "title" => Value::text(title),
103 "marks" => Value::text(marks_str),
104 "visible_marks" => Value::text(visible_marks_str),
105 });
106 }
107
108 api.set_widget(widget)?;
109 }
110}
111
112#[async_trait]
113trait Backend {
114 async fn get_info(&mut self) -> Result<Info>;
115}
116
117#[derive(Clone, Default)]
118struct Info {
119 title: String,
120 marks: Vec<String>,
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn plan_declares_single_output_without_icons() {
129 let plan = prepare(&Config::default()).unwrap();
130 assert_eq!(plan.outputs().count(), 1);
131 let main = plan.output("main").unwrap();
132 assert!(main.format().contains_key("title"));
133 assert_eq!(main.output().icon_placeholders().count(), 0);
134 }
135
136 #[test]
137 fn custom_format_is_used() {
138 let config = Config {
139 format: " $visible_marks ".parse().unwrap(),
140 ..Config::default()
141 };
142 let plan = prepare(&config).unwrap();
143 let main = plan.output("main").unwrap();
144 assert!(main.format().contains_key("visible_marks"));
145 assert!(!main.format().contains_key("title"));
146 }
147}