summaryrefslogtreecommitdiff
path: root/src/status_cmd.rs
blob: e77e4b2aa83a9b08cee5ae822e6e9885db276fd6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use crate::Surface;
use std::cell::RefCell;
use std::io::Result;
use std::io::{BufRead, BufReader, Write};
use std::os::unix::prelude::{AsRawFd, RawFd};
use std::process::{ChildStdin, ChildStdout, Command, Stdio};
use std::rc::Rc;

use crate::i3bar_protocol::{Block, Event, Protocol};
use crate::pointer_btn::PointerBtn;

#[derive(Clone)]
pub struct StatusCmd {
    inner: Rc<RefCell<Inner>>,
}

struct Inner {
    output: BufReader<ChildStdout>,
    input: ChildStdin,
    protocol: Protocol,
    blocks: Rc<RefCell<Vec<Block>>>,
    surfaces: Rc<RefCell<Vec<Surface>>>,
}

impl AsRawFd for StatusCmd {
    fn as_raw_fd(&self) -> RawFd {
        self.inner.borrow().output.get_ref().as_raw_fd()
    }
}

impl Inner {
    pub fn notify_available(&mut self) -> Result<()> {
        let buf = self.output.fill_buf()?;
        self.protocol.process_new_bytes(buf)?;
        let consumed = buf.len();
        self.output.consume(consumed);
        if let Some(new_blocks) = self.protocol.get_blocks() {
            *self.blocks.borrow_mut() = new_blocks;
            for s in &mut *self.surfaces.borrow_mut() {
                s.blocks_need_update = true;
            }
        }
        Ok(())
    }
}

impl StatusCmd {
    pub fn new(
        cmd: &str,
        blocks: Rc<RefCell<Vec<Block>>>,
        surfaces: Rc<RefCell<Vec<Surface>>>,
    ) -> Result<Self> {
        let mut child = Command::new("sh")
            .args(["-c", &format!("exec {cmd}")])
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()?;
        let output = BufReader::new(child.stdout.take().unwrap());
        let input = child.stdin.take().unwrap();
        Ok(Self {
            inner: Rc::new(RefCell::new(Inner {
                output,
                input,
                protocol: Protocol::Unknown,
                blocks,
                surfaces,
            })),
        })
    }

    pub fn notify_available(&mut self) -> Result<()> {
        self.inner.borrow_mut().notify_available()
    }

    pub fn send_click_event(
        &mut self,
        button: PointerBtn,
        name: Option<&str>,
        instance: Option<&str>,
    ) -> Result<()> {
        let mut inner = self.inner.borrow_mut();
        if inner.protocol.supports_clicks() {
            writeln!(
                inner.input,
                "{}",
                serde_json::to_string(&Event {
                    name,
                    instance,
                    button,
                    ..Default::default()
                })
                .unwrap()
            )?;
        }
        Ok(())
    }
}