use std::io::prelude::*; use std::io::BufReader; use std::process::{Command, Stdio}; use std::thread; use ui::draw_context::DrawContext; use widgets::{Message, MessageSender, Widget}; const MARGIN: u16 = 7; struct Desktop { name: String, selected: bool, urgent: bool, position: u16, width: u16 } impl Desktop { fn contains(&self, x: u16) -> bool { x >= self.position && x < self.position + self.width } } pub struct Bspwm { context: DrawContext, tx: MessageSender, desktops: Vec } impl Bspwm { pub fn new(tx: MessageSender, context: DrawContext) -> Bspwm { Bspwm { context: context, tx: tx, desktops: vec![] } } fn parse_bspwm(&mut self, line: &str) { let (kind, line) = line.split_at(1); if kind != "W" { return }; let mut desktops = vec![]; let elems = line.split(':'); let mut pos = 0; for elem in elems { let mut chars = elem.chars(); let kind = chars.next().unwrap(); let name = chars.collect::(); 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 { let width = self.context.measure_text(&name) + MARGIN * 2; desktops.push(Desktop { name: name, selected: selected, urgent: urgent, position: pos, width: width }); pos += width; } } } self.desktops = desktops; } } impl Widget for Bspwm { fn init(&mut self) { let tx = self.tx.clone(); thread::spawn(move || monitor_thread(tx)); } fn render(&mut self, x: u16, _w: u16) { for desktop in self.desktops.iter() { 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(x + desktop.position, desktop.width); self.context.draw_text(&desktop.name, x + desktop.position + MARGIN); } } 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: &Message) -> bool { match event { &Message::BspwmEvent(ref line) => { self.parse_bspwm(line); self.tx.send(Message::Relayout).expect("Failed to send relayout"); }, &Message::MousePress(x) => { for desktop in self.desktops.iter() { if desktop.contains(x) { switch_desktop(&desktop.name); } } }, _ => {} } false } } fn monitor_thread(tx: MessageSender) { 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 event = Message::BspwmEvent(line.clone()); tx.send(event).ok(); } } fn switch_desktop(name: &str) { Command::new("bspc") .arg("desktop") .arg("-f") .arg(name) .output() .ok(); }