1use std::process::Command;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::time::Duration;
9
10const POLL: Duration = Duration::from_millis(200);
11const FORMAT: &str = "#{session_attached} #{window_active} #{pane_active} #{window_zoomed_flag}";
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum PaneView {
16 Active,
18 Frozen,
21 Hidden,
23}
24
25pub(crate) fn watch(
28 program: String,
29 stop: Arc<AtomicBool>,
30 report: impl Fn(PaneView) -> bool + Send + 'static,
31) {
32 std::thread::spawn(move || {
33 let mut shown = PaneView::Active;
34 while !stop.load(Ordering::Relaxed) {
35 std::thread::sleep(POLL);
36 let Some(view) = query(&program) else {
37 continue;
38 };
39 if view == shown {
40 continue;
41 }
42 shown = view;
43 if !report(view) {
44 return;
45 }
46 }
47 });
48}
49
50fn query(program: &str) -> Option<PaneView> {
51 let pane = std::env::var("TMUX_PANE").ok()?;
52 let output = Command::new(program)
53 .args(["display-message", "-p", "-t", &pane, FORMAT])
54 .output()
55 .ok()?;
56 parse(std::str::from_utf8(&output.stdout).ok()?)
57}
58
59fn parse(fields: &str) -> Option<PaneView> {
60 let mut values = fields.split_whitespace();
61 let mut flag = || values.next()?.parse::<u32>().ok();
62 let attached = flag()?;
63 let window_active = flag()?;
64 let pane_active = flag()?;
65 let zoomed = flag()?;
66 Some(
67 if attached == 0 || window_active == 0 || (zoomed == 1 && pane_active == 0) {
68 PaneView::Hidden
69 } else if pane_active == 0 {
70 PaneView::Frozen
71 } else {
72 PaneView::Active
73 },
74 )
75}
76
77#[cfg(test)]
78mod tests {
79 use super::{PaneView, parse};
80
81 #[test]
82 fn a_pane_view_follows_focus_window_and_zoom() {
83 assert_eq!(parse("1 1 1 0\n"), Some(PaneView::Active));
84 assert_eq!(parse("1 1 0 0"), Some(PaneView::Frozen));
85 assert_eq!(parse("1 0 1 0"), Some(PaneView::Hidden));
86 assert_eq!(parse("1 1 0 1"), Some(PaneView::Hidden));
87 assert_eq!(parse("1 1 1 1"), Some(PaneView::Active));
88 assert_eq!(parse("0 1 1 0"), Some(PaneView::Hidden));
89 assert_eq!(parse("error"), None);
90 }
91}