1use std::io::Read;
2use std::marker::{Send, Sync};
3use std::os::unix::net::UnixListener;
4use std::sync::mpsc;
5use std::sync::{Arc, Mutex};
6use std::thread;
7use std::time::{Duration, Instant};
8
9const SOCKET_PATH: &str = "/tmp/dwm-statusbar.sock";
10
11type BlockFn = Box<dyn Fn(u32) -> String + Send + Sync>;
12
13pub struct StatusBlock {
14 pub f: BlockFn,
15 pub interval: Duration,
16}
17
18impl StatusBlock {
19 pub fn new(f: BlockFn, int_millis: u64) -> Self {
20 Self {
21 f: Box::new(f),
22 interval: Duration::from_millis(int_millis),
23 }
24 }
25}
26
27#[macro_export]
28macro_rules! blocks {
29 [ $({$f:expr, $int:expr}),* $(,)? ] => {
30 vec![$($crate::StatusBlock::new(Box::new($f), $int)),*]
31 };
32}
33
34pub struct StatusBar {
35 separator: &'static str,
36 windows: Vec<Vec<Option<StatusBlock>>>,
37 placeholder: Arc<Mutex<Vec<String>>>,
39 window_index: Arc<Mutex<u32>>,
40
41 tx: mpsc::Sender<()>,
42 rx: mpsc::Receiver<()>,
43}
44
45impl StatusBar {
46 pub fn new(
47 separator: &'static str,
48 default_window: u32,
49 windows: Vec<Vec<StatusBlock>>,
50 ) -> Self {
51 let (tx, rx) = mpsc::channel();
52
53 Self {
54 placeholder: Arc::new(Mutex::new(vec![
55 String::new();
56 windows[default_window as usize].len()
57 ])),
58 separator,
59 windows: windows
60 .into_iter()
61 .map(|i| i.into_iter().map(Some).collect())
62 .collect(),
63 window_index: Arc::new(Mutex::new(default_window)),
65
66 tx,
67 rx,
68 }
69 }
70
71 pub fn start(&mut self) {
73 for (current_window_idx, window) in self.windows.iter_mut().enumerate() {
74 for (placeholder_index, block) in window.iter_mut().enumerate() {
75 let placeholder = Arc::clone(&self.placeholder);
76 let block = block.take().unwrap();
77 let tx = self.tx.clone();
78 let display_window_index = Arc::clone(&self.window_index);
79
80 thread::spawn(move || {
81 let mut i = 0;
82 loop {
83 if *display_window_index.lock().unwrap() != current_window_idx as u32 {
84 thread::sleep(block.interval);
85 continue;
86 }
87
88 let start = Instant::now();
89 let output = (block.f)(i);
90
91 if *display_window_index.lock().unwrap() != current_window_idx as u32 {
93 thread::sleep(block.interval);
94 continue;
95 }
96
97 placeholder.lock().unwrap()[placeholder_index] = output;
98 tx.send(()).unwrap();
99
100 let time = start.elapsed();
101 if block.interval >= time {
102 thread::sleep(block.interval - time);
103 }
104
105 i += 1;
106 }
107 });
108 }
109 }
110
111 let max_window_idx = self.windows.len();
112 let window_index = Arc::clone(&self.window_index);
113 let placeholder = Arc::clone(&self.placeholder);
114 let window_sizes = self.windows.iter().map(|i| i.len()).collect::<Vec<_>>();
115 let tx = self.tx.clone();
116
117 thread::spawn(move || {
118 let mut previous_state = vec![vec![]; max_window_idx + 1];
119 let _ = std::fs::remove_file(SOCKET_PATH);
120 let listener = UnixListener::bind(SOCKET_PATH).unwrap();
121
122 loop {
123 println!("Listening for connection");
124 for mut stream in listener.incoming().flatten() {
125 let mut buf = String::new();
126
127 if stream.read_to_string(&mut buf).is_err() {
128 eprintln!("[SocketError] Failed to read");
129 continue;
130 };
131
132 let Ok(window_idx) = buf.trim().parse::<u32>() else {
133 eprintln!("[SocketError] Invalid input received");
134 continue;
135 };
136
137 if window_idx > max_window_idx as u32
138 || window_sizes.len() <= window_idx as usize
139 {
140 eprintln!("[SocketError] Invalid window index");
141 continue;
142 }
143
144 let mut wi = window_index.lock().unwrap();
145 let previous_window_idx = *wi;
146 *wi = window_idx;
147 drop(wi);
148
149 let mut placeholder = placeholder.lock().unwrap();
150
151 previous_state[previous_window_idx as usize] = placeholder.clone();
153
154 let ps = &mut previous_state[window_idx as usize];
155 *placeholder = if !ps.is_empty() {
156 ps.to_vec()
157 } else {
158 vec![String::from("loading..."); window_sizes[window_idx as usize]]
159 };
160
161 tx.send(()).unwrap();
162 }
163 }
164 });
165
166 while self.rx.recv().is_ok() {
167 self.update_status();
168 }
169 }
170
171 fn update_status(&self) {
172 let output = self.placeholder.lock().unwrap().join(self.separator);
173 std::process::Command::new("xsetroot")
175 .arg("-name")
176 .arg(output)
177 .spawn()
178 .unwrap()
179 .wait()
180 .expect("failed on wait");
181 }
183}