|  | @@ -0,0 +1,138 @@
 | 
	
		
			
				|  |  | +use std::io::prelude::*;
 | 
	
		
			
				|  |  | +use std::io::BufReader;
 | 
	
		
			
				|  |  | +use std::process::{Command, Stdio};
 | 
	
		
			
				|  |  | +use std::sync::mpsc;
 | 
	
		
			
				|  |  | +use std::thread;
 | 
	
		
			
				|  |  | +use ui::widget;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const MARGIN: u16 = 7;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct Desktop {
 | 
	
		
			
				|  |  | +    name: String,
 | 
	
		
			
				|  |  | +    selected: bool,
 | 
	
		
			
				|  |  | +    urgent: bool
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +pub struct Bspwm {
 | 
	
		
			
				|  |  | +    context: widget::DrawContext,
 | 
	
		
			
				|  |  | +    tx: mpsc::Sender<widget::WidgetMessage>,
 | 
	
		
			
				|  |  | +    desktops: Vec<Desktop>
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +impl Bspwm {
 | 
	
		
			
				|  |  | +    pub fn new(tx: mpsc::Sender<widget::WidgetMessage>, context: widget::DrawContext) -> Bspwm {
 | 
	
		
			
				|  |  | +        Bspwm {
 | 
	
		
			
				|  |  | +            context: context,
 | 
	
		
			
				|  |  | +            tx: tx,
 | 
	
		
			
				|  |  | +            desktops: vec![]
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +impl widget::Widget for Bspwm {
 | 
	
		
			
				|  |  | +    fn init(&mut self) {
 | 
	
		
			
				|  |  | +        let tx = self.tx.clone();
 | 
	
		
			
				|  |  | +        thread::spawn(move || monitor_thread(tx));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fn render(&mut self, x: u16) {
 | 
	
		
			
				|  |  | +        let mut offset = x;
 | 
	
		
			
				|  |  | +        for desktop in self.desktops.iter() {
 | 
	
		
			
				|  |  | +            let text_width = self.context.measure_text(&desktop.name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if desktop.selected {
 | 
	
		
			
				|  |  | +                self.context.set_bg_color(0x6666, 0xCCCC, 0x6666, 0xFFFF);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else if desktop.urgent {
 | 
	
		
			
				|  |  | +                self.context.set_bg_color(0xCCCC, 0x0, 0x3333, 0xFFFF);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else {
 | 
	
		
			
				|  |  | +                self.context.set_bg_color(0x0, 0x0, 0x0, 0xFFFF);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            self.context.draw_bg(offset, text_width + MARGIN * 2);
 | 
	
		
			
				|  |  | +            self.context.draw_text(&desktop.name, offset + MARGIN);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            offset += text_width + MARGIN * 2;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fn width(&mut self) -> u16 {
 | 
	
		
			
				|  |  | +        let mut sum = 0;
 | 
	
		
			
				|  |  | +        for desktop in self.desktops.iter() {
 | 
	
		
			
				|  |  | +            sum += self.context.measure_text(&desktop.name) + MARGIN * 2;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        sum
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fn handle_event(&mut self, event: &widget::WidgetMessage) -> bool {
 | 
	
		
			
				|  |  | +        match event {
 | 
	
		
			
				|  |  | +            &widget::WidgetMessage::BspwmEvent(ref line) => {
 | 
	
		
			
				|  |  | +                self.desktops = parse_bspwm(line);
 | 
	
		
			
				|  |  | +                self.tx.send(widget::WidgetMessage::Relayout).expect("Failed to send relayout");
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +            _ => {}
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        false
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +fn monitor_thread(tx: mpsc::Sender<widget::WidgetMessage>) {
 | 
	
		
			
				|  |  | +    let bspc = Command::new("bspc")
 | 
	
		
			
				|  |  | +        .arg("subscribe")
 | 
	
		
			
				|  |  | +        .arg("report")
 | 
	
		
			
				|  |  | +        .stdout(Stdio::piped())
 | 
	
		
			
				|  |  | +        .spawn()
 | 
	
		
			
				|  |  | +        .ok()
 | 
	
		
			
				|  |  | +        .expect("Failed to start bspc subscribe");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let stdout = bspc.stdout.unwrap();
 | 
	
		
			
				|  |  | +    let mut reader = BufReader::new(stdout);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let mut line = String::new();
 | 
	
		
			
				|  |  | +    loop {
 | 
	
		
			
				|  |  | +        line.clear();
 | 
	
		
			
				|  |  | +        reader.read_line(&mut line).ok().expect("Failed to read line");
 | 
	
		
			
				|  |  | +        let kind = line.remove(0);
 | 
	
		
			
				|  |  | +        let line = line.trim();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        match kind {
 | 
	
		
			
				|  |  | +            'W' => {
 | 
	
		
			
				|  |  | +                let event = widget::WidgetMessage::BspwmEvent(line.to_string());
 | 
	
		
			
				|  |  | +                tx.send(event).expect("Failed to send bswpm event")
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +            _ => {}
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +fn parse_bspwm(line: &str) -> Vec<Desktop> {
 | 
	
		
			
				|  |  | +    let mut desktops = vec![];
 | 
	
		
			
				|  |  | +    let elems = line.split(':');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for elem in elems {
 | 
	
		
			
				|  |  | +        let mut chars = elem.chars();
 | 
	
		
			
				|  |  | +        let kind = chars.next().unwrap();
 | 
	
		
			
				|  |  | +        let name = chars.collect::<String>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if kind == 'M' || kind == 'm' {}
 | 
	
		
			
				|  |  | +        else if kind == 'L' {}
 | 
	
		
			
				|  |  | +        else if kind == 'G' {}
 | 
	
		
			
				|  |  | +        else if kind == 'T' {}
 | 
	
		
			
				|  |  | +        else {
 | 
	
		
			
				|  |  | +            let empty = kind == 'f';
 | 
	
		
			
				|  |  | +            let urgent = kind == 'U' || kind == 'u';
 | 
	
		
			
				|  |  | +            let selected = kind.is_uppercase();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if !empty {
 | 
	
		
			
				|  |  | +                desktops.push(Desktop {
 | 
	
		
			
				|  |  | +                    name: name,
 | 
	
		
			
				|  |  | +                    selected: selected,
 | 
	
		
			
				|  |  | +                    urgent: urgent
 | 
	
		
			
				|  |  | +                })
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    desktops
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 |